aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/tests/api
diff options
context:
space:
mode:
Diffstat (limited to 'server/tests/api')
-rw-r--r--server/tests/api/check-params/config.ts9
-rw-r--r--server/tests/api/check-params/index.ts1
-rw-r--r--server/tests/api/check-params/live.ts8
-rw-r--r--server/tests/api/check-params/runners.ts2
-rw-r--r--server/tests/api/check-params/transcoding.ts14
-rw-r--r--server/tests/api/check-params/video-files.ts44
-rw-r--r--server/tests/api/check-params/video-imports.ts4
-rw-r--r--server/tests/api/check-params/video-passwords.ts609
-rw-r--r--server/tests/api/check-params/video-playlists.ts4
-rw-r--r--server/tests/api/check-params/video-storyboards.ts45
-rw-r--r--server/tests/api/check-params/video-studio.ts6
-rw-r--r--server/tests/api/check-params/video-token.ts44
-rw-r--r--server/tests/api/check-params/videos-overviews.ts2
-rw-r--r--server/tests/api/check-params/videos.ts8
-rw-r--r--server/tests/api/live/live.ts6
-rw-r--r--server/tests/api/object-storage/video-static-file-privacy.ts150
-rw-r--r--server/tests/api/object-storage/videos.ts42
-rw-r--r--server/tests/api/redundancy/redundancy.ts26
-rw-r--r--server/tests/api/runners/runner-studio-transcoding.ts2
-rw-r--r--server/tests/api/runners/runner-vod-transcoding.ts2
-rw-r--r--server/tests/api/server/config.ts11
-rw-r--r--server/tests/api/server/follows.ts949
-rw-r--r--server/tests/api/server/stats.ts2
-rw-r--r--server/tests/api/transcoding/audio-only.ts8
-rw-r--r--server/tests/api/transcoding/create-transcoding.ts18
-rw-r--r--server/tests/api/transcoding/hls.ts8
-rw-r--r--server/tests/api/transcoding/transcoder.ts42
-rw-r--r--server/tests/api/transcoding/update-while-transcoding.ts4
-rw-r--r--server/tests/api/transcoding/video-studio.ts10
-rw-r--r--server/tests/api/users/user-videos.ts4
-rw-r--r--server/tests/api/users/users.ts24
-rw-r--r--server/tests/api/videos/index.ts1
-rw-r--r--server/tests/api/videos/multiple-servers.ts25
-rw-r--r--server/tests/api/videos/resumable-upload.ts42
-rw-r--r--server/tests/api/videos/single-server.ts4
-rw-r--r--server/tests/api/videos/video-files.ts34
-rw-r--r--server/tests/api/videos/video-imports.ts14
-rw-r--r--server/tests/api/videos/video-passwords.ts97
-rw-r--r--server/tests/api/videos/video-playlist-thumbnails.ts18
-rw-r--r--server/tests/api/videos/video-playlists.ts33
-rw-r--r--server/tests/api/videos/video-static-file-privacy.ts129
-rw-r--r--server/tests/api/videos/video-storyboard.ts213
-rw-r--r--server/tests/api/videos/videos-common-filters.ts34
43 files changed, 2019 insertions, 733 deletions
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index 472cad182..80b616ccf 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -74,6 +74,9 @@ describe('Test config API validators', function () {
74 }, 74 },
75 torrents: { 75 torrents: {
76 size: 4 76 size: 4
77 },
78 storyboards: {
79 size: 5
77 } 80 }
78 }, 81 },
79 signup: { 82 signup: {
@@ -123,7 +126,7 @@ describe('Test config API validators', function () {
123 '2160p': false 126 '2160p': false
124 }, 127 },
125 alwaysTranscodeOriginalResolution: false, 128 alwaysTranscodeOriginalResolution: false,
126 webtorrent: { 129 webVideos: {
127 enabled: true 130 enabled: true
128 }, 131 },
129 hls: { 132 hls: {
@@ -342,7 +345,7 @@ describe('Test config API validators', function () {
342 }) 345 })
343 }) 346 })
344 347
345 it('Should fail with a disabled webtorrent & hls transcoding', async function () { 348 it('Should fail with a disabled web videos & hls transcoding', async function () {
346 const newUpdateParams = { 349 const newUpdateParams = {
347 ...updateParams, 350 ...updateParams,
348 351
@@ -350,7 +353,7 @@ describe('Test config API validators', function () {
350 hls: { 353 hls: {
351 enabled: false 354 enabled: false
352 }, 355 },
353 webtorrent: { 356 web_videos: {
354 enabled: false 357 enabled: false
355 } 358 }
356 } 359 }
diff --git a/server/tests/api/check-params/index.ts b/server/tests/api/check-params/index.ts
index 400d312d3..c2a7ccd78 100644
--- a/server/tests/api/check-params/index.ts
+++ b/server/tests/api/check-params/index.ts
@@ -34,6 +34,7 @@ import './video-comments'
34import './video-files' 34import './video-files'
35import './video-imports' 35import './video-imports'
36import './video-playlists' 36import './video-playlists'
37import './video-storyboards'
37import './video-source' 38import './video-source'
38import './video-studio' 39import './video-studio'
39import './video-token' 40import './video-token'
diff --git a/server/tests/api/check-params/live.ts b/server/tests/api/check-params/live.ts
index 2dc735c23..5021db516 100644
--- a/server/tests/api/check-params/live.ts
+++ b/server/tests/api/check-params/live.ts
@@ -143,7 +143,7 @@ describe('Test video lives API validator', function () {
143 }) 143 })
144 144
145 it('Should fail with a bad privacy for replay settings', async function () { 145 it('Should fail with a bad privacy for replay settings', async function () {
146 const fields = { ...baseCorrectParams, replaySettings: { privacy: 5 } } 146 const fields = { ...baseCorrectParams, saveReplay: true, replaySettings: { privacy: 999 } }
147 147
148 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 148 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
149 }) 149 })
@@ -194,7 +194,7 @@ describe('Test video lives API validator', function () {
194 it('Should fail with a big thumbnail file', async function () { 194 it('Should fail with a big thumbnail file', async function () {
195 const fields = baseCorrectParams 195 const fields = baseCorrectParams
196 const attaches = { 196 const attaches = {
197 thumbnailfile: buildAbsoluteFixturePath('preview-big.png') 197 thumbnailfile: buildAbsoluteFixturePath('custom-preview-big.png')
198 } 198 }
199 199
200 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches }) 200 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
@@ -212,7 +212,7 @@ describe('Test video lives API validator', function () {
212 it('Should fail with a big preview file', async function () { 212 it('Should fail with a big preview file', async function () {
213 const fields = baseCorrectParams 213 const fields = baseCorrectParams
214 const attaches = { 214 const attaches = {
215 previewfile: buildAbsoluteFixturePath('preview-big.png') 215 previewfile: buildAbsoluteFixturePath('custom-preview-big.png')
216 } 216 }
217 217
218 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches }) 218 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
@@ -472,7 +472,7 @@ describe('Test video lives API validator', function () {
472 }) 472 })
473 473
474 it('Should fail with a bad privacy for replay settings', async function () { 474 it('Should fail with a bad privacy for replay settings', async function () {
475 const fields = { saveReplay: true, replaySettings: { privacy: 5 } } 475 const fields = { saveReplay: true, replaySettings: { privacy: 999 } }
476 476
477 await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 477 await command.update({ videoId: video.id, fields, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
478 }) 478 })
diff --git a/server/tests/api/check-params/runners.ts b/server/tests/api/check-params/runners.ts
index 48821b678..4ba90802f 100644
--- a/server/tests/api/check-params/runners.ts
+++ b/server/tests/api/check-params/runners.ts
@@ -752,7 +752,7 @@ describe('Test managing runners', function () {
752 }) 752 })
753 753
754 it('Should fail with an invalid vod audio merge payload', async function () { 754 it('Should fail with an invalid vod audio merge payload', async function () {
755 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 755 const attributes = { name: 'audio_with_preview', previewfile: 'custom-preview.jpg', fixture: 'sample.ogg' }
756 await server.videos.upload({ attributes, mode: 'legacy' }) 756 await server.videos.upload({ attributes, mode: 'legacy' })
757 757
758 await waitJobs([ server ]) 758 await waitJobs([ server ])
diff --git a/server/tests/api/check-params/transcoding.ts b/server/tests/api/check-params/transcoding.ts
index 9846ac182..4bebcb528 100644
--- a/server/tests/api/check-params/transcoding.ts
+++ b/server/tests/api/check-params/transcoding.ts
@@ -49,21 +49,21 @@ describe('Test transcoding API validators', function () {
49 49
50 it('Should not run transcoding of a unknown video', async function () { 50 it('Should not run transcoding of a unknown video', async function () {
51 await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'hls', expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 51 await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'hls', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
52 await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'webtorrent', expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 52 await servers[0].videos.runTranscoding({ videoId: 404, transcodingType: 'web-video', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
53 }) 53 })
54 54
55 it('Should not run transcoding of a remote video', async function () { 55 it('Should not run transcoding of a remote video', async function () {
56 const expectedStatus = HttpStatusCode.BAD_REQUEST_400 56 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
57 57
58 await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'hls', expectedStatus }) 58 await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'hls', expectedStatus })
59 await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'webtorrent', expectedStatus }) 59 await servers[0].videos.runTranscoding({ videoId: remoteId, transcodingType: 'web-video', expectedStatus })
60 }) 60 })
61 61
62 it('Should not run transcoding by a non admin user', async function () { 62 it('Should not run transcoding by a non admin user', async function () {
63 const expectedStatus = HttpStatusCode.FORBIDDEN_403 63 const expectedStatus = HttpStatusCode.FORBIDDEN_403
64 64
65 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', token: userToken, expectedStatus }) 65 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', token: userToken, expectedStatus })
66 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent', token: moderatorToken, expectedStatus }) 66 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', token: moderatorToken, expectedStatus })
67 }) 67 })
68 68
69 it('Should not run transcoding without transcoding type', async function () { 69 it('Should not run transcoding without transcoding type', async function () {
@@ -82,7 +82,7 @@ describe('Test transcoding API validators', function () {
82 await servers[0].config.disableTranscoding() 82 await servers[0].config.disableTranscoding()
83 83
84 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', expectedStatus }) 84 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls', expectedStatus })
85 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent', expectedStatus }) 85 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', expectedStatus })
86 }) 86 })
87 87
88 it('Should run transcoding', async function () { 88 it('Should run transcoding', async function () {
@@ -93,15 +93,15 @@ describe('Test transcoding API validators', function () {
93 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls' }) 93 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'hls' })
94 await waitJobs(servers) 94 await waitJobs(servers)
95 95
96 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent' }) 96 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' })
97 await waitJobs(servers) 97 await waitJobs(servers)
98 }) 98 })
99 99
100 it('Should not run transcoding on a video that is already being transcoded', async function () { 100 it('Should not run transcoding on a video that is already being transcoded', async function () {
101 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent' }) 101 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video' })
102 102
103 const expectedStatus = HttpStatusCode.CONFLICT_409 103 const expectedStatus = HttpStatusCode.CONFLICT_409
104 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'webtorrent', expectedStatus }) 104 await servers[0].videos.runTranscoding({ videoId: validId, transcodingType: 'web-video', expectedStatus })
105 }) 105 })
106 106
107 after(async function () { 107 after(async function () {
diff --git a/server/tests/api/check-params/video-files.ts b/server/tests/api/check-params/video-files.ts
index 9dc59a1b5..4d43ab6f8 100644
--- a/server/tests/api/check-params/video-files.ts
+++ b/server/tests/api/check-params/video-files.ts
@@ -60,7 +60,7 @@ describe('Test videos files', function () {
60 }) 60 })
61 61
62 describe('Deleting files', function () { 62 describe('Deleting files', function () {
63 let webtorrentId: string 63 let webVideoId: string
64 let hlsId: string 64 let hlsId: string
65 let remoteId: string 65 let remoteId: string
66 66
@@ -68,10 +68,10 @@ describe('Test videos files', function () {
68 let validId2: string 68 let validId2: string
69 69
70 let hlsFileId: number 70 let hlsFileId: number
71 let webtorrentFileId: number 71 let webVideoFileId: number
72 72
73 let remoteHLSFileId: number 73 let remoteHLSFileId: number
74 let remoteWebtorrentFileId: number 74 let remoteWebVideoFileId: number
75 75
76 before(async function () { 76 before(async function () {
77 this.timeout(300_000) 77 this.timeout(300_000)
@@ -83,7 +83,7 @@ describe('Test videos files', function () {
83 const video = await servers[1].videos.get({ id: uuid }) 83 const video = await servers[1].videos.get({ id: uuid })
84 remoteId = video.uuid 84 remoteId = video.uuid
85 remoteHLSFileId = video.streamingPlaylists[0].files[0].id 85 remoteHLSFileId = video.streamingPlaylists[0].files[0].id
86 remoteWebtorrentFileId = video.files[0].id 86 remoteWebVideoFileId = video.files[0].id
87 } 87 }
88 88
89 { 89 {
@@ -96,7 +96,7 @@ describe('Test videos files', function () {
96 const video = await servers[0].videos.get({ id: uuid }) 96 const video = await servers[0].videos.get({ id: uuid })
97 validId1 = video.uuid 97 validId1 = video.uuid
98 hlsFileId = video.streamingPlaylists[0].files[0].id 98 hlsFileId = video.streamingPlaylists[0].files[0].id
99 webtorrentFileId = video.files[0].id 99 webVideoFileId = video.files[0].id
100 } 100 }
101 101
102 { 102 {
@@ -117,8 +117,8 @@ describe('Test videos files', function () {
117 117
118 { 118 {
119 await servers[0].config.enableTranscoding(false, true) 119 await servers[0].config.enableTranscoding(false, true)
120 const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' }) 120 const { uuid } = await servers[0].videos.quickUpload({ name: 'web-video' })
121 webtorrentId = uuid 121 webVideoId = uuid
122 } 122 }
123 123
124 await waitJobs(servers) 124 await waitJobs(servers)
@@ -128,27 +128,27 @@ describe('Test videos files', function () {
128 const expectedStatus = HttpStatusCode.NOT_FOUND_404 128 const expectedStatus = HttpStatusCode.NOT_FOUND_404
129 129
130 await servers[0].videos.removeHLSPlaylist({ videoId: 404, expectedStatus }) 130 await servers[0].videos.removeHLSPlaylist({ videoId: 404, expectedStatus })
131 await servers[0].videos.removeAllWebTorrentFiles({ videoId: 404, expectedStatus }) 131 await servers[0].videos.removeAllWebVideoFiles({ videoId: 404, expectedStatus })
132 132
133 await servers[0].videos.removeHLSFile({ videoId: 404, fileId: hlsFileId, expectedStatus }) 133 await servers[0].videos.removeHLSFile({ videoId: 404, fileId: hlsFileId, expectedStatus })
134 await servers[0].videos.removeWebTorrentFile({ videoId: 404, fileId: webtorrentFileId, expectedStatus }) 134 await servers[0].videos.removeWebVideoFile({ videoId: 404, fileId: webVideoFileId, expectedStatus })
135 }) 135 })
136 136
137 it('Should not delete unknown files', async function () { 137 it('Should not delete unknown files', async function () {
138 const expectedStatus = HttpStatusCode.NOT_FOUND_404 138 const expectedStatus = HttpStatusCode.NOT_FOUND_404
139 139
140 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: webtorrentFileId, expectedStatus }) 140 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: webVideoFileId, expectedStatus })
141 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: hlsFileId, expectedStatus }) 141 await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: hlsFileId, expectedStatus })
142 }) 142 })
143 143
144 it('Should not delete files of a remote video', async function () { 144 it('Should not delete files of a remote video', async function () {
145 const expectedStatus = HttpStatusCode.BAD_REQUEST_400 145 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
146 146
147 await servers[0].videos.removeHLSPlaylist({ videoId: remoteId, expectedStatus }) 147 await servers[0].videos.removeHLSPlaylist({ videoId: remoteId, expectedStatus })
148 await servers[0].videos.removeAllWebTorrentFiles({ videoId: remoteId, expectedStatus }) 148 await servers[0].videos.removeAllWebVideoFiles({ videoId: remoteId, expectedStatus })
149 149
150 await servers[0].videos.removeHLSFile({ videoId: remoteId, fileId: remoteHLSFileId, expectedStatus }) 150 await servers[0].videos.removeHLSFile({ videoId: remoteId, fileId: remoteHLSFileId, expectedStatus })
151 await servers[0].videos.removeWebTorrentFile({ videoId: remoteId, fileId: remoteWebtorrentFileId, expectedStatus }) 151 await servers[0].videos.removeWebVideoFile({ videoId: remoteId, fileId: remoteWebVideoFileId, expectedStatus })
152 }) 152 })
153 153
154 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 () {
@@ -157,35 +157,35 @@ describe('Test videos files', function () {
157 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: userToken, expectedStatus }) 157 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: userToken, expectedStatus })
158 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: moderatorToken, expectedStatus }) 158 await servers[0].videos.removeHLSPlaylist({ videoId: validId1, token: moderatorToken, expectedStatus })
159 159
160 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: userToken, expectedStatus }) 160 await servers[0].videos.removeAllWebVideoFiles({ videoId: validId1, token: userToken, expectedStatus })
161 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1, token: moderatorToken, expectedStatus }) 161 await servers[0].videos.removeAllWebVideoFiles({ videoId: validId1, token: moderatorToken, expectedStatus })
162 162
163 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 })
164 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 })
165 165
166 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: userToken, expectedStatus }) 166 await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: webVideoFileId, token: userToken, expectedStatus })
167 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId, token: moderatorToken, expectedStatus }) 167 await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: webVideoFileId, token: moderatorToken, expectedStatus })
168 }) 168 })
169 169
170 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 () {
171 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 })
172 await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 172 await servers[0].videos.removeAllWebVideoFiles({ videoId: webVideoId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
173 173
174 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 })
175 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 175 await servers[0].videos.removeWebVideoFile({ videoId: webVideoId, fileId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
176 }) 176 })
177 177
178 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 () {
179 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 })
180 await servers[0].videos.removeAllWebTorrentFiles({ videoId: webtorrentId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 }) 180 await servers[0].videos.removeAllWebVideoFiles({ videoId: webVideoId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
181 }) 181 })
182 182
183 it('Should delete files if both versions are available', async function () { 183 it('Should delete files if both versions are available', async function () {
184 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId }) 184 await servers[0].videos.removeHLSFile({ videoId: validId1, fileId: hlsFileId })
185 await servers[0].videos.removeWebTorrentFile({ videoId: validId1, fileId: webtorrentFileId }) 185 await servers[0].videos.removeWebVideoFile({ videoId: validId1, fileId: webVideoFileId })
186 186
187 await servers[0].videos.removeHLSPlaylist({ videoId: validId1 }) 187 await servers[0].videos.removeHLSPlaylist({ videoId: validId1 })
188 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId2 }) 188 await servers[0].videos.removeAllWebVideoFiles({ videoId: validId2 })
189 }) 189 })
190 }) 190 })
191 191
diff --git a/server/tests/api/check-params/video-imports.ts b/server/tests/api/check-params/video-imports.ts
index 7f19b9ee9..8c6f43c12 100644
--- a/server/tests/api/check-params/video-imports.ts
+++ b/server/tests/api/check-params/video-imports.ts
@@ -244,7 +244,7 @@ describe('Test video imports API validator', function () {
244 it('Should fail with a big thumbnail file', async function () { 244 it('Should fail with a big thumbnail file', async function () {
245 const fields = baseCorrectParams 245 const fields = baseCorrectParams
246 const attaches = { 246 const attaches = {
247 thumbnailfile: buildAbsoluteFixturePath('preview-big.png') 247 thumbnailfile: buildAbsoluteFixturePath('custom-preview-big.png')
248 } 248 }
249 249
250 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches }) 250 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
@@ -262,7 +262,7 @@ describe('Test video imports API validator', function () {
262 it('Should fail with a big preview file', async function () { 262 it('Should fail with a big preview file', async function () {
263 const fields = baseCorrectParams 263 const fields = baseCorrectParams
264 const attaches = { 264 const attaches = {
265 previewfile: buildAbsoluteFixturePath('preview-big.png') 265 previewfile: buildAbsoluteFixturePath('custom-preview-big.png')
266 } 266 }
267 267
268 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches }) 268 await makeUploadRequest({ url: server.url, path, token: server.accessToken, fields, attaches })
diff --git a/server/tests/api/check-params/video-passwords.ts b/server/tests/api/check-params/video-passwords.ts
new file mode 100644
index 000000000..4e936b5d2
--- /dev/null
+++ b/server/tests/api/check-params/video-passwords.ts
@@ -0,0 +1,609 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2import {
3 FIXTURE_URLS,
4 checkBadCountPagination,
5 checkBadSortPagination,
6 checkBadStartPagination,
7 checkUploadVideoParam
8} from '@server/tests/shared'
9import { root } from '@shared/core-utils'
10import {
11 HttpStatusCode,
12 PeerTubeProblemDocument,
13 ServerErrorCode,
14 VideoCreateResult,
15 VideoPrivacy
16} from '@shared/models'
17import {
18 cleanupTests,
19 createSingleServer,
20 makePostBodyRequest,
21 PeerTubeServer,
22 setAccessTokensToServers
23} from '@shared/server-commands'
24import { expect } from 'chai'
25import { join } from 'path'
26
27describe('Test video passwords validator', function () {
28 let path: string
29 let server: PeerTubeServer
30 let userAccessToken = ''
31 let video: VideoCreateResult
32 let channelId: number
33 let publicVideo: VideoCreateResult
34 let commentId: number
35 // ---------------------------------------------------------------
36
37 before(async function () {
38 this.timeout(50000)
39
40 server = await createSingleServer(1)
41
42 await setAccessTokensToServers([ server ])
43
44 await server.config.updateCustomSubConfig({
45 newConfig: {
46 live: {
47 enabled: true,
48 latencySetting: {
49 enabled: false
50 },
51 allowReplay: false
52 },
53 import: {
54 videos: {
55 http:{
56 enabled: true
57 }
58 }
59 }
60 }
61 })
62
63 userAccessToken = await server.users.generateUserAndToken('user1')
64
65 {
66 const body = await server.users.getMyInfo()
67 channelId = body.videoChannels[0].id
68 }
69
70 {
71 video = await server.videos.quickUpload({
72 name: 'password protected video',
73 privacy: VideoPrivacy.PASSWORD_PROTECTED,
74 videoPasswords: [ 'password1', 'password2' ]
75 })
76 }
77 path = '/api/v1/videos/'
78 })
79
80 async function checkVideoPasswordOptions (options: {
81 server: PeerTubeServer
82 token: string
83 videoPasswords: string[]
84 expectedStatus: HttpStatusCode
85 mode: 'uploadLegacy' | 'uploadResumable' | 'import' | 'updateVideo' | 'updatePasswords' | 'live'
86 }) {
87 const { server, token, videoPasswords, expectedStatus = HttpStatusCode.OK_200, mode } = options
88 const attaches = {
89 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.webm')
90 }
91 const baseCorrectParams = {
92 name: 'my super name',
93 category: 5,
94 licence: 1,
95 language: 'pt',
96 nsfw: false,
97 commentsEnabled: true,
98 downloadEnabled: true,
99 waitTranscoding: true,
100 description: 'my super description',
101 support: 'my super support text',
102 tags: [ 'tag1', 'tag2' ],
103 privacy: VideoPrivacy.PASSWORD_PROTECTED,
104 channelId,
105 originallyPublishedAt: new Date().toISOString()
106 }
107 if (mode === 'uploadLegacy') {
108 const fields = { ...baseCorrectParams, videoPasswords }
109 return checkUploadVideoParam(server, token, { ...fields, ...attaches }, expectedStatus, 'legacy')
110 }
111
112 if (mode === 'uploadResumable') {
113 const fields = { ...baseCorrectParams, videoPasswords }
114 return checkUploadVideoParam(server, token, { ...fields, ...attaches }, expectedStatus, 'resumable')
115 }
116
117 if (mode === 'import') {
118 const attributes = { ...baseCorrectParams, targetUrl: FIXTURE_URLS.goodVideo, videoPasswords }
119 return server.imports.importVideo({ attributes, expectedStatus })
120 }
121
122 if (mode === 'updateVideo') {
123 const attributes = { ...baseCorrectParams, videoPasswords }
124 return server.videos.update({ token, expectedStatus, id: video.id, attributes })
125 }
126
127 if (mode === 'updatePasswords') {
128 return server.videoPasswords.updateAll({ token, expectedStatus, videoId: video.id, passwords: videoPasswords })
129 }
130
131 if (mode === 'live') {
132 const fields = { ...baseCorrectParams, videoPasswords }
133
134 return server.live.create({ fields, expectedStatus })
135 }
136 }
137
138 function validateVideoPasswordList (mode: 'uploadLegacy' | 'uploadResumable' | 'import' | 'updateVideo' | 'updatePasswords' | 'live') {
139
140 it('Should fail with a password protected privacy without providing a password', async function () {
141 await checkVideoPasswordOptions({
142 server,
143 token: server.accessToken,
144 videoPasswords: undefined,
145 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
146 mode
147 })
148 })
149
150 it('Should fail with a password protected privacy and an empty password list', async function () {
151 const videoPasswords = []
152
153 await checkVideoPasswordOptions({
154 server,
155 token: server.accessToken,
156 videoPasswords,
157 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
158 mode
159 })
160 })
161
162 it('Should fail with a password protected privacy and a too short password', async function () {
163 const videoPasswords = [ 'p' ]
164
165 await checkVideoPasswordOptions({
166 server,
167 token: server.accessToken,
168 videoPasswords,
169 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
170 mode
171 })
172 })
173
174 it('Should fail with a password protected privacy and a too long password', async function () {
175 const videoPasswords = [ 'Very very very very very very very very very very very very very very very very very very long password' ]
176
177 await checkVideoPasswordOptions({
178 server,
179 token: server.accessToken,
180 videoPasswords,
181 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
182 mode
183 })
184 })
185
186 it('Should fail with a password protected privacy and an empty password', async function () {
187 const videoPasswords = [ '' ]
188
189 await checkVideoPasswordOptions({
190 server,
191 token: server.accessToken,
192 videoPasswords,
193 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
194 mode
195 })
196 })
197
198 it('Should fail with a password protected privacy and duplicated passwords', async function () {
199 const videoPasswords = [ 'password', 'password' ]
200
201 await checkVideoPasswordOptions({
202 server,
203 token: server.accessToken,
204 videoPasswords,
205 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
206 mode
207 })
208 })
209
210 if (mode === 'updatePasswords') {
211 it('Should fail for an unauthenticated user', async function () {
212 const videoPasswords = [ 'password' ]
213 await checkVideoPasswordOptions({
214 server,
215 token: null,
216 videoPasswords,
217 expectedStatus: HttpStatusCode.UNAUTHORIZED_401,
218 mode
219 })
220 })
221
222 it('Should fail for an unauthorized user', async function () {
223 const videoPasswords = [ 'password' ]
224 await checkVideoPasswordOptions({
225 server,
226 token: userAccessToken,
227 videoPasswords,
228 expectedStatus: HttpStatusCode.FORBIDDEN_403,
229 mode
230 })
231 })
232 }
233
234 it('Should succeed with a password protected privacy and correct passwords', async function () {
235 const videoPasswords = [ 'password1', 'password2' ]
236 const expectedStatus = mode === 'updatePasswords' || mode === 'updateVideo'
237 ? HttpStatusCode.NO_CONTENT_204
238 : HttpStatusCode.OK_200
239
240 await checkVideoPasswordOptions({ server, token: server.accessToken, videoPasswords, expectedStatus, mode })
241 })
242 }
243
244 describe('When adding or updating a video', function () {
245 describe('Resumable upload', function () {
246 validateVideoPasswordList('uploadResumable')
247 })
248
249 describe('Legacy upload', function () {
250 validateVideoPasswordList('uploadLegacy')
251 })
252
253 describe('When importing a video', function () {
254 validateVideoPasswordList('import')
255 })
256
257 describe('When updating a video', function () {
258 validateVideoPasswordList('updateVideo')
259 })
260
261 describe('When updating the password list of a video', function () {
262 validateVideoPasswordList('updatePasswords')
263 })
264
265 describe('When creating a live', function () {
266 validateVideoPasswordList('live')
267 })
268 })
269
270 async function checkVideoAccessOptions (options: {
271 server: PeerTubeServer
272 token?: string
273 videoPassword?: string
274 expectedStatus: HttpStatusCode
275 mode: 'get' | 'getWithPassword' | 'getWithToken' | 'listCaptions' | 'createThread' | 'listThreads' | 'replyThread' | 'rate' | 'token'
276 }) {
277 const { server, token = null, videoPassword, expectedStatus, mode } = options
278
279 if (mode === 'get') {
280 return server.videos.get({ id: video.id, expectedStatus })
281 }
282
283 if (mode === 'getWithToken') {
284 return server.videos.getWithToken({
285 id: video.id,
286 token,
287 expectedStatus
288 })
289 }
290
291 if (mode === 'getWithPassword') {
292 return server.videos.getWithPassword({
293 id: video.id,
294 token,
295 expectedStatus,
296 password: videoPassword
297 })
298 }
299
300 if (mode === 'rate') {
301 return server.videos.rate({
302 id: video.id,
303 token,
304 expectedStatus,
305 rating: 'like',
306 videoPassword
307 })
308 }
309
310 if (mode === 'createThread') {
311 const fields = { text: 'super comment' }
312 const headers = videoPassword !== undefined && videoPassword !== null
313 ? { 'x-peertube-video-password': videoPassword }
314 : undefined
315 const body = await makePostBodyRequest({
316 url: server.url,
317 path: path + video.uuid + '/comment-threads',
318 token,
319 fields,
320 headers,
321 expectedStatus
322 })
323 return JSON.parse(body.text)
324 }
325
326 if (mode === 'replyThread') {
327 const fields = { text: 'super reply' }
328 const headers = videoPassword !== undefined && videoPassword !== null
329 ? { 'x-peertube-video-password': videoPassword }
330 : undefined
331 return makePostBodyRequest({
332 url: server.url,
333 path: path + video.uuid + '/comments/' + commentId,
334 token,
335 fields,
336 headers,
337 expectedStatus
338 })
339 }
340 if (mode === 'listThreads') {
341 return server.comments.listThreads({
342 videoId: video.id,
343 token,
344 expectedStatus,
345 videoPassword
346 })
347 }
348
349 if (mode === 'listCaptions') {
350 return server.captions.list({
351 videoId: video.id,
352 token,
353 expectedStatus,
354 videoPassword
355 })
356 }
357
358 if (mode === 'token') {
359 return server.videoToken.create({
360 videoId: video.id,
361 token,
362 expectedStatus,
363 videoPassword
364 })
365 }
366 }
367
368 function checkVideoError (error: any, mode: 'providePassword' | 'incorrectPassword') {
369 const serverCode = mode === 'providePassword'
370 ? ServerErrorCode.VIDEO_REQUIRES_PASSWORD
371 : ServerErrorCode.INCORRECT_VIDEO_PASSWORD
372
373 const message = mode === 'providePassword'
374 ? 'Please provide a password to access this password protected video'
375 : 'Incorrect video password. Access to the video is denied.'
376
377 if (!error.code) {
378 error = JSON.parse(error.text)
379 }
380
381 expect(error.code).to.equal(serverCode)
382 expect(error.detail).to.equal(message)
383 expect(error.error).to.equal(message)
384
385 expect(error.status).to.equal(HttpStatusCode.FORBIDDEN_403)
386 }
387
388 function validateVideoAccess (mode: 'get' | 'listCaptions' | 'createThread' | 'listThreads' | 'replyThread' | 'rate' | 'token') {
389 const requiresUserAuth = [ 'createThread', 'replyThread', 'rate' ].includes(mode)
390 let tokens: string[]
391 if (!requiresUserAuth) {
392 it('Should fail without providing a password for an unlogged user', async function () {
393 const body = await checkVideoAccessOptions({ server, expectedStatus: HttpStatusCode.FORBIDDEN_403, mode })
394 const error = body as unknown as PeerTubeProblemDocument
395
396 checkVideoError(error, 'providePassword')
397 })
398 }
399
400 it('Should fail without providing a password for an unauthorised user', async function () {
401 const tmp = mode === 'get' ? 'getWithToken' : mode
402
403 const body = await checkVideoAccessOptions({
404 server,
405 token: userAccessToken,
406 expectedStatus: HttpStatusCode.FORBIDDEN_403,
407 mode: tmp
408 })
409
410 const error = body as unknown as PeerTubeProblemDocument
411
412 checkVideoError(error, 'providePassword')
413 })
414
415 it('Should fail if a wrong password is entered', async function () {
416 const tmp = mode === 'get' ? 'getWithPassword' : mode
417 tokens = [ userAccessToken, server.accessToken ]
418
419 if (!requiresUserAuth) tokens.push(null)
420
421 for (const token of tokens) {
422 const body = await checkVideoAccessOptions({
423 server,
424 token,
425 videoPassword: 'toto',
426 expectedStatus: HttpStatusCode.FORBIDDEN_403,
427 mode: tmp
428 })
429 const error = body as unknown as PeerTubeProblemDocument
430
431 checkVideoError(error, 'incorrectPassword')
432 }
433 })
434
435 it('Should fail if an empty password is entered', async function () {
436 const tmp = mode === 'get' ? 'getWithPassword' : mode
437
438 for (const token of tokens) {
439 const body = await checkVideoAccessOptions({
440 server,
441 token,
442 videoPassword: '',
443 expectedStatus: HttpStatusCode.FORBIDDEN_403,
444 mode: tmp
445 })
446 const error = body as unknown as PeerTubeProblemDocument
447
448 checkVideoError(error, 'incorrectPassword')
449 }
450 })
451
452 it('Should fail if an inccorect password containing the correct password is entered', async function () {
453 const tmp = mode === 'get' ? 'getWithPassword' : mode
454
455 for (const token of tokens) {
456 const body = await checkVideoAccessOptions({
457 server,
458 token,
459 videoPassword: 'password11',
460 expectedStatus: HttpStatusCode.FORBIDDEN_403,
461 mode: tmp
462 })
463 const error = body as unknown as PeerTubeProblemDocument
464
465 checkVideoError(error, 'incorrectPassword')
466 }
467 })
468
469 it('Should succeed without providing a password for an authorised user', async function () {
470 const tmp = mode === 'get' ? 'getWithToken' : mode
471 const expectedStatus = mode === 'rate' ? HttpStatusCode.NO_CONTENT_204 : HttpStatusCode.OK_200
472
473 const body = await checkVideoAccessOptions({ server, token: server.accessToken, expectedStatus, mode: tmp })
474
475 if (mode === 'createThread') commentId = body.comment.id
476 })
477
478 it('Should succeed using correct passwords', async function () {
479 const tmp = mode === 'get' ? 'getWithPassword' : mode
480 const expectedStatus = mode === 'rate' ? HttpStatusCode.NO_CONTENT_204 : HttpStatusCode.OK_200
481
482 for (const token of tokens) {
483 await checkVideoAccessOptions({ server, videoPassword: 'password1', token, expectedStatus, mode: tmp })
484 await checkVideoAccessOptions({ server, videoPassword: 'password2', token, expectedStatus, mode: tmp })
485 }
486 })
487 }
488
489 describe('When accessing password protected video', function () {
490
491 describe('For getting a password protected video', function () {
492 validateVideoAccess('get')
493 })
494
495 describe('For rating a video', function () {
496 validateVideoAccess('rate')
497 })
498
499 describe('For creating a thread', function () {
500 validateVideoAccess('createThread')
501 })
502
503 describe('For replying to a thread', function () {
504 validateVideoAccess('replyThread')
505 })
506
507 describe('For listing threads', function () {
508 validateVideoAccess('listThreads')
509 })
510
511 describe('For getting captions', function () {
512 validateVideoAccess('listCaptions')
513 })
514
515 describe('For creating video file token', function () {
516 validateVideoAccess('token')
517 })
518 })
519
520 describe('When listing passwords', function () {
521 it('Should fail with a bad start pagination', async function () {
522 await checkBadStartPagination(server.url, path + video.uuid + '/passwords', server.accessToken)
523 })
524
525 it('Should fail with a bad count pagination', async function () {
526 await checkBadCountPagination(server.url, path + video.uuid + '/passwords', server.accessToken)
527 })
528
529 it('Should fail with an incorrect sort', async function () {
530 await checkBadSortPagination(server.url, path + video.uuid + '/passwords', server.accessToken)
531 })
532
533 it('Should fail for unauthenticated user', async function () {
534 await server.videoPasswords.list({
535 token: null,
536 expectedStatus: HttpStatusCode.UNAUTHORIZED_401,
537 videoId: video.id
538 })
539 })
540
541 it('Should fail for unauthorized user', async function () {
542 await server.videoPasswords.list({
543 token: userAccessToken,
544 expectedStatus: HttpStatusCode.FORBIDDEN_403,
545 videoId: video.id
546 })
547 })
548
549 it('Should succeed with the correct parameters', async function () {
550 await server.videoPasswords.list({
551 token: server.accessToken,
552 expectedStatus: HttpStatusCode.OK_200,
553 videoId: video.id
554 })
555 })
556 })
557
558 describe('When deleting a password', async function () {
559 const passwords = (await server.videoPasswords.list({ videoId: video.id })).data
560
561 it('Should fail with wrong password id', async function () {
562 await server.videoPasswords.remove({ id: -1, videoId: video.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
563 })
564
565 it('Should fail for unauthenticated user', async function () {
566 await server.videoPasswords.remove({
567 id: passwords[0].id,
568 token: null,
569 videoId: video.id,
570 expectedStatus: HttpStatusCode.FORBIDDEN_403
571 })
572 })
573
574 it('Should fail for unauthorized user', async function () {
575 await server.videoPasswords.remove({
576 id: passwords[0].id,
577 token: userAccessToken,
578 videoId: video.id,
579 expectedStatus: HttpStatusCode.BAD_REQUEST_400
580 })
581 })
582
583 it('Should fail for non password protected video', async function () {
584 publicVideo = await server.videos.quickUpload({ name: 'public video' })
585 await server.videoPasswords.remove({ id: passwords[0].id, videoId: publicVideo.id, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
586 })
587
588 it('Should fail for password not linked to correct video', async function () {
589 const video2 = await server.videos.quickUpload({
590 name: 'password protected video',
591 privacy: VideoPrivacy.PASSWORD_PROTECTED,
592 videoPasswords: [ 'password1', 'password2' ]
593 })
594 await server.videoPasswords.remove({ id: passwords[0].id, videoId: video2.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
595 })
596
597 it('Should succeed with correct parameter', async function () {
598 await server.videoPasswords.remove({ id: passwords[0].id, videoId: video.id, expectedStatus: HttpStatusCode.NO_CONTENT_204 })
599 })
600
601 it('Should fail for last password of a video', async function () {
602 await server.videoPasswords.remove({ id: passwords[1].id, videoId: video.id, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
603 })
604 })
605
606 after(async function () {
607 await cleanupTests([ server ])
608 })
609})
diff --git a/server/tests/api/check-params/video-playlists.ts b/server/tests/api/check-params/video-playlists.ts
index 8090897c1..8c3233e0b 100644
--- a/server/tests/api/check-params/video-playlists.ts
+++ b/server/tests/api/check-params/video-playlists.ts
@@ -196,7 +196,7 @@ describe('Test video playlists API validator', function () {
196 attributes: { 196 attributes: {
197 displayName: 'display name', 197 displayName: 'display name',
198 privacy: VideoPlaylistPrivacy.UNLISTED, 198 privacy: VideoPlaylistPrivacy.UNLISTED,
199 thumbnailfile: 'thumbnail.jpg', 199 thumbnailfile: 'custom-thumbnail.jpg',
200 videoChannelId: server.store.channel.id, 200 videoChannelId: server.store.channel.id,
201 201
202 ...attributes 202 ...attributes
@@ -260,7 +260,7 @@ describe('Test video playlists API validator', function () {
260 }) 260 })
261 261
262 it('Should fail with a thumbnail file too big', async function () { 262 it('Should fail with a thumbnail file too big', async function () {
263 const params = getBase({ thumbnailfile: 'preview-big.png' }) 263 const params = getBase({ thumbnailfile: 'custom-preview-big.png' })
264 264
265 await command.create(params) 265 await command.create(params)
266 await command.update(getUpdate(params, playlist.shortUUID)) 266 await command.update(getUpdate(params, playlist.shortUUID))
diff --git a/server/tests/api/check-params/video-storyboards.ts b/server/tests/api/check-params/video-storyboards.ts
new file mode 100644
index 000000000..c038e7370
--- /dev/null
+++ b/server/tests/api/check-params/video-storyboards.ts
@@ -0,0 +1,45 @@
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 storyboards API validator', function () {
7 let server: PeerTubeServer
8
9 let publicVideo: { uuid: string }
10 let privateVideo: { uuid: string }
11
12 // ---------------------------------------------------------------
13
14 before(async function () {
15 this.timeout(120000)
16
17 server = await createSingleServer(1)
18 await setAccessTokensToServers([ server ])
19
20 publicVideo = await server.videos.quickUpload({ name: 'public' })
21 privateVideo = await server.videos.quickUpload({ name: 'private', privacy: VideoPrivacy.PRIVATE })
22 })
23
24 it('Should fail without a valid uuid', async function () {
25 await server.storyboard.list({ id: '4da6fde3-88f7-4d16-b119-108df563d0b0', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
26 })
27
28 it('Should receive 404 when passing a non existing video id', async function () {
29 await server.storyboard.list({ id: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
30 })
31
32 it('Should not get the private storyboard without the appropriate token', async function () {
33 await server.storyboard.list({ id: privateVideo.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401, token: null })
34 await server.storyboard.list({ id: publicVideo.uuid, expectedStatus: HttpStatusCode.OK_200, token: null })
35 })
36
37 it('Should succeed with the correct parameters', async function () {
38 await server.storyboard.list({ id: privateVideo.uuid })
39 await server.storyboard.list({ id: publicVideo.uuid })
40 })
41
42 after(async function () {
43 await cleanupTests([ server ])
44 })
45})
diff --git a/server/tests/api/check-params/video-studio.ts b/server/tests/api/check-params/video-studio.ts
index add8d9164..4ac0d93ed 100644
--- a/server/tests/api/check-params/video-studio.ts
+++ b/server/tests/api/check-params/video-studio.ts
@@ -293,7 +293,7 @@ describe('Test video studio API validator', function () {
293 it('Should succeed with the correct params', async function () { 293 it('Should succeed with the correct params', async function () {
294 this.timeout(120000) 294 this.timeout(120000)
295 295
296 await addWatermark('thumbnail.jpg', HttpStatusCode.NO_CONTENT_204) 296 await addWatermark('custom-thumbnail.jpg', HttpStatusCode.NO_CONTENT_204)
297 297
298 await waitJobs([ server ]) 298 await waitJobs([ server ])
299 }) 299 })
@@ -322,8 +322,8 @@ describe('Test video studio API validator', function () {
322 }) 322 })
323 323
324 it('Should fail with an invalid file', async function () { 324 it('Should fail with an invalid file', async function () {
325 await addIntroOutro('add-intro', 'thumbnail.jpg') 325 await addIntroOutro('add-intro', 'custom-thumbnail.jpg')
326 await addIntroOutro('add-outro', 'thumbnail.jpg') 326 await addIntroOutro('add-outro', 'custom-thumbnail.jpg')
327 }) 327 })
328 328
329 it('Should fail with a file that does not contain video stream', async function () { 329 it('Should fail with a file that does not contain video stream', async function () {
diff --git a/server/tests/api/check-params/video-token.ts b/server/tests/api/check-params/video-token.ts
index 7acb9d580..7cb3e84a2 100644
--- a/server/tests/api/check-params/video-token.ts
+++ b/server/tests/api/check-params/video-token.ts
@@ -5,9 +5,12 @@ import { cleanupTests, createSingleServer, PeerTubeServer, setAccessTokensToServ
5 5
6describe('Test video tokens', function () { 6describe('Test video tokens', function () {
7 let server: PeerTubeServer 7 let server: PeerTubeServer
8 let videoId: string 8 let privateVideoId: string
9 let passwordProtectedVideoId: string
9 let userToken: string 10 let userToken: string
10 11
12 const videoPassword = 'password'
13
11 // --------------------------------------------------------------- 14 // ---------------------------------------------------------------
12 15
13 before(async function () { 16 before(async function () {
@@ -15,27 +18,50 @@ describe('Test video tokens', function () {
15 18
16 server = await createSingleServer(1) 19 server = await createSingleServer(1)
17 await setAccessTokensToServers([ server ]) 20 await setAccessTokensToServers([ server ])
18 21 {
19 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) 22 const { uuid } = await server.videos.quickUpload({ name: 'private video', privacy: VideoPrivacy.PRIVATE })
20 videoId = uuid 23 privateVideoId = uuid
21 24 }
25 {
26 const { uuid } = await server.videos.quickUpload({
27 name: 'password protected video',
28 privacy: VideoPrivacy.PASSWORD_PROTECTED,
29 videoPasswords: [ videoPassword ]
30 })
31 passwordProtectedVideoId = uuid
32 }
22 userToken = await server.users.generateUserAndToken('user1') 33 userToken = await server.users.generateUserAndToken('user1')
23 }) 34 })
24 35
25 it('Should not generate tokens for unauthenticated user', async function () { 36 it('Should not generate tokens on private video for unauthenticated user', async function () {
26 await server.videoToken.create({ videoId, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) 37 await server.videoToken.create({ videoId: privateVideoId, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
27 }) 38 })
28 39
29 it('Should not generate tokens of unknown video', async function () { 40 it('Should not generate tokens of unknown video', async function () {
30 await server.videoToken.create({ videoId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) 41 await server.videoToken.create({ videoId: 404, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
31 }) 42 })
32 43
44 it('Should not generate tokens with incorrect password', async function () {
45 await server.videoToken.create({
46 videoId: passwordProtectedVideoId,
47 token: null,
48 expectedStatus: HttpStatusCode.FORBIDDEN_403,
49 videoPassword: 'incorrectPassword'
50 })
51 })
52
33 it('Should not generate tokens of a non owned video', async function () { 53 it('Should not generate tokens of a non owned video', async function () {
34 await server.videoToken.create({ videoId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 54 await server.videoToken.create({ videoId: privateVideoId, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
35 }) 55 })
36 56
37 it('Should generate token', async function () { 57 it('Should generate token', async function () {
38 await server.videoToken.create({ videoId }) 58 await server.videoToken.create({ videoId: privateVideoId })
59 })
60
61 it('Should generate token on password protected video', async function () {
62 await server.videoToken.create({ videoId: passwordProtectedVideoId, videoPassword, token: null })
63 await server.videoToken.create({ videoId: passwordProtectedVideoId, videoPassword, token: userToken })
64 await server.videoToken.create({ videoId: passwordProtectedVideoId, videoPassword })
39 }) 65 })
40 66
41 after(async function () { 67 after(async function () {
diff --git a/server/tests/api/check-params/videos-overviews.ts b/server/tests/api/check-params/videos-overviews.ts
index f9cdb7ab3..ae7de24dd 100644
--- a/server/tests/api/check-params/videos-overviews.ts
+++ b/server/tests/api/check-params/videos-overviews.ts
@@ -2,7 +2,7 @@
2 2
3import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/server-commands' 3import { cleanupTests, createSingleServer, PeerTubeServer } from '@shared/server-commands'
4 4
5describe('Test videos overview', function () { 5describe('Test videos overview API validator', function () {
6 let server: PeerTubeServer 6 let server: PeerTubeServer
7 7
8 // --------------------------------------------------------------- 8 // ---------------------------------------------------------------
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index 094ab6891..6ee1955a7 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -384,7 +384,7 @@ describe('Test videos API validator', function () {
384 it('Should fail with a big thumbnail file', async function () { 384 it('Should fail with a big thumbnail file', async function () {
385 const fields = baseCorrectParams 385 const fields = baseCorrectParams
386 const attaches = { 386 const attaches = {
387 thumbnailfile: join(root(), 'server', 'tests', 'fixtures', 'preview-big.png'), 387 thumbnailfile: join(root(), 'server', 'tests', 'fixtures', 'custom-preview-big.png'),
388 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 388 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
389 } 389 }
390 390
@@ -404,7 +404,7 @@ describe('Test videos API validator', function () {
404 it('Should fail with a big preview file', async function () { 404 it('Should fail with a big preview file', async function () {
405 const fields = baseCorrectParams 405 const fields = baseCorrectParams
406 const attaches = { 406 const attaches = {
407 previewfile: join(root(), 'server', 'tests', 'fixtures', 'preview-big.png'), 407 previewfile: join(root(), 'server', 'tests', 'fixtures', 'custom-preview-big.png'),
408 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4') 408 fixture: join(root(), 'server', 'tests', 'fixtures', 'video_short.mp4')
409 } 409 }
410 410
@@ -615,7 +615,7 @@ describe('Test videos API validator', function () {
615 it('Should fail with a big thumbnail file', async function () { 615 it('Should fail with a big thumbnail file', async function () {
616 const fields = baseCorrectParams 616 const fields = baseCorrectParams
617 const attaches = { 617 const attaches = {
618 thumbnailfile: join(root(), 'server', 'tests', 'fixtures', 'preview-big.png') 618 thumbnailfile: join(root(), 'server', 'tests', 'fixtures', 'custom-preview-big.png')
619 } 619 }
620 620
621 await makeUploadRequest({ 621 await makeUploadRequest({
@@ -647,7 +647,7 @@ describe('Test videos API validator', function () {
647 it('Should fail with a big preview file', async function () { 647 it('Should fail with a big preview file', async function () {
648 const fields = baseCorrectParams 648 const fields = baseCorrectParams
649 const attaches = { 649 const attaches = {
650 previewfile: join(root(), 'server', 'tests', 'fixtures', 'preview-big.png') 650 previewfile: join(root(), 'server', 'tests', 'fixtures', 'custom-preview-big.png')
651 } 651 }
652 652
653 await makeUploadRequest({ 653 await makeUploadRequest({
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 7ab67b126..2b302a8a2 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -2,7 +2,7 @@
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { basename, join } from 'path' 4import { basename, join } from 'path'
5import { SQLCommand, testImage, testLiveVideoResolutions } from '@server/tests/shared' 5import { SQLCommand, testImageGeneratedByFFmpeg, testLiveVideoResolutions } from '@server/tests/shared'
6import { getAllFiles, wait } from '@shared/core-utils' 6import { getAllFiles, wait } from '@shared/core-utils'
7import { ffprobePromise, getVideoStream } from '@shared/ffmpeg' 7import { ffprobePromise, getVideoStream } from '@shared/ffmpeg'
8import { 8import {
@@ -121,8 +121,8 @@ describe('Test live', function () {
121 expect(video.downloadEnabled).to.be.false 121 expect(video.downloadEnabled).to.be.false
122 expect(video.privacy.id).to.equal(VideoPrivacy.PUBLIC) 122 expect(video.privacy.id).to.equal(VideoPrivacy.PUBLIC)
123 123
124 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 124 await testImageGeneratedByFFmpeg(server.url, 'video_short1-preview.webm', video.previewPath)
125 await testImage(server.url, 'video_short1.webm', video.thumbnailPath) 125 await testImageGeneratedByFFmpeg(server.url, 'video_short1.webm', video.thumbnailPath)
126 126
127 const live = await server.live.get({ videoId: liveVideoUUID }) 127 const live = await server.live.get({ videoId: liveVideoUUID })
128 128
diff --git a/server/tests/api/object-storage/video-static-file-privacy.ts b/server/tests/api/object-storage/video-static-file-privacy.ts
index af9d681b2..64ab542a5 100644
--- a/server/tests/api/object-storage/video-static-file-privacy.ts
+++ b/server/tests/api/object-storage/video-static-file-privacy.ts
@@ -39,7 +39,7 @@ describe('Object storage for video static file privacy', function () {
39 const video = await server.videos.getWithToken({ id: uuid }) 39 const video = await server.videos.getWithToken({ id: uuid })
40 40
41 for (const file of video.files) { 41 for (const file of video.files) {
42 expectStartWith(file.fileUrl, server.url + '/object-storage-proxy/webseed/private/') 42 expectStartWith(file.fileUrl, server.url + '/object-storage-proxy/web-videos/private/')
43 43
44 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) 44 await makeRawRequest({ url: file.fileUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
45 } 45 }
@@ -107,15 +107,20 @@ describe('Object storage for video static file privacy', function () {
107 describe('VOD', function () { 107 describe('VOD', function () {
108 let privateVideoUUID: string 108 let privateVideoUUID: string
109 let publicVideoUUID: string 109 let publicVideoUUID: string
110 let passwordProtectedVideoUUID: string
110 let userPrivateVideoUUID: string 111 let userPrivateVideoUUID: string
111 112
113 const correctPassword = 'my super password'
114 const correctPasswordHeader = { 'x-peertube-video-password': correctPassword }
115 const incorrectPasswordHeader = { 'x-peertube-video-password': correctPassword + 'toto' }
116
112 // --------------------------------------------------------------------------- 117 // ---------------------------------------------------------------------------
113 118
114 async function getSampleFileUrls (videoId: string) { 119 async function getSampleFileUrls (videoId: string) {
115 const video = await server.videos.getWithToken({ id: videoId }) 120 const video = await server.videos.getWithToken({ id: videoId })
116 121
117 return { 122 return {
118 webTorrentFile: video.files[0].fileUrl, 123 webVideoFile: video.files[0].fileUrl,
119 hlsFile: getHLS(video).files[0].fileUrl 124 hlsFile: getHLS(video).files[0].fileUrl
120 } 125 }
121 } 126 }
@@ -140,6 +145,22 @@ describe('Object storage for video static file privacy', function () {
140 await checkPrivateVODFiles(privateVideoUUID) 145 await checkPrivateVODFiles(privateVideoUUID)
141 }) 146 })
142 147
148 it('Should upload a password protected video and have appropriate object storage ACL', async function () {
149 this.timeout(120000)
150
151 {
152 const { uuid } = await server.videos.quickUpload({
153 name: 'video',
154 privacy: VideoPrivacy.PASSWORD_PROTECTED,
155 videoPasswords: [ correctPassword ]
156 })
157 passwordProtectedVideoUUID = uuid
158 }
159 await waitJobs([ server ])
160
161 await checkPrivateVODFiles(passwordProtectedVideoUUID)
162 })
163
143 it('Should upload a public video and have appropriate object storage ACL', async function () { 164 it('Should upload a public video and have appropriate object storage ACL', async function () {
144 this.timeout(120000) 165 this.timeout(120000)
145 166
@@ -154,13 +175,49 @@ describe('Object storage for video static file privacy', function () {
154 it('Should not get files without appropriate OAuth token', async function () { 175 it('Should not get files without appropriate OAuth token', async function () {
155 this.timeout(60000) 176 this.timeout(60000)
156 177
157 const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID) 178 const { webVideoFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
179
180 await makeRawRequest({ url: webVideoFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
181 await makeRawRequest({ url: webVideoFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
182
183 await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
184 await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
185 })
186
187 it('Should not get files without appropriate password or appropriate OAuth token', async function () {
188 this.timeout(60000)
189
190 const { webVideoFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID)
158 191
159 await makeRawRequest({ url: webTorrentFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 192 await makeRawRequest({ url: webVideoFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
160 await makeRawRequest({ url: webTorrentFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) 193 await makeRawRequest({
194 url: webVideoFile,
195 token: null,
196 headers: incorrectPasswordHeader,
197 expectedStatus: HttpStatusCode.FORBIDDEN_403
198 })
199 await makeRawRequest({ url: webVideoFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
200 await makeRawRequest({
201 url: webVideoFile,
202 token: null,
203 headers: correctPasswordHeader,
204 expectedStatus: HttpStatusCode.OK_200
205 })
161 206
162 await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 207 await makeRawRequest({ url: hlsFile, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
208 await makeRawRequest({
209 url: hlsFile,
210 token: null,
211 headers: incorrectPasswordHeader,
212 expectedStatus: HttpStatusCode.FORBIDDEN_403
213 })
163 await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) 214 await makeRawRequest({ url: hlsFile, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
215 await makeRawRequest({
216 url: hlsFile,
217 token: null,
218 headers: correctPasswordHeader,
219 expectedStatus: HttpStatusCode.OK_200
220 })
164 }) 221 })
165 222
166 it('Should not get HLS file of another video', async function () { 223 it('Should not get HLS file of another video', async function () {
@@ -176,21 +233,50 @@ describe('Object storage for video static file privacy', function () {
176 await makeRawRequest({ url: goodUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) 233 await makeRawRequest({ url: goodUrl, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
177 }) 234 })
178 235
179 it('Should correctly check OAuth or video file token', async function () { 236 it('Should correctly check OAuth, video file token of private video', async function () {
180 this.timeout(60000) 237 this.timeout(60000)
181 238
182 const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID }) 239 const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID })
183 const goodVideoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID }) 240 const goodVideoFileToken = await server.videoToken.getVideoFileToken({ videoId: privateVideoUUID })
184 241
185 const { webTorrentFile, hlsFile } = await getSampleFileUrls(privateVideoUUID) 242 const { webVideoFile, hlsFile } = await getSampleFileUrls(privateVideoUUID)
243
244 for (const url of [ webVideoFile, hlsFile ]) {
245 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
246 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
247 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
248
249 await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
250 await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 })
251
252 }
253 })
254
255 it('Should correctly check OAuth, video file token or video password of password protected video', async function () {
256 this.timeout(60000)
186 257
187 for (const url of [ webTorrentFile, hlsFile ]) { 258 const badVideoFileToken = await server.videoToken.getVideoFileToken({ token: userToken, videoId: userPrivateVideoUUID })
259 const goodVideoFileToken = await server.videoToken.getVideoFileToken({
260 videoId: passwordProtectedVideoUUID,
261 videoPassword: correctPassword
262 })
263
264 const { webVideoFile, hlsFile } = await getSampleFileUrls(passwordProtectedVideoUUID)
265
266 for (const url of [ hlsFile, webVideoFile ]) {
188 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 267 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
189 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 268 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
190 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) 269 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
191 270
192 await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 271 await makeRawRequest({ url, query: { videoFileToken: badVideoFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
193 await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 }) 272 await makeRawRequest({ url, query: { videoFileToken: goodVideoFileToken }, expectedStatus: HttpStatusCode.OK_200 })
273
274 await makeRawRequest({
275 url,
276 headers: incorrectPasswordHeader,
277 expectedStatus: HttpStatusCode.FORBIDDEN_403
278 })
279 await makeRawRequest({ url, headers: correctPasswordHeader, expectedStatus: HttpStatusCode.OK_200 })
194 } 280 }
195 }) 281 })
196 282
@@ -232,16 +318,26 @@ describe('Object storage for video static file privacy', function () {
232 let permanentLiveId: string 318 let permanentLiveId: string
233 let permanentLive: LiveVideo 319 let permanentLive: LiveVideo
234 320
321 let passwordProtectedLiveId: string
322 let passwordProtectedLive: LiveVideo
323
324 const correctPassword = 'my super password'
325
235 let unrelatedFileToken: string 326 let unrelatedFileToken: string
236 327
237 // --------------------------------------------------------------------------- 328 // ---------------------------------------------------------------------------
238 329
239 async function checkLiveFiles (live: LiveVideo, liveId: string) { 330 async function checkLiveFiles (live: LiveVideo, liveId: string, videoPassword?: string) {
240 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) 331 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
241 await server.live.waitUntilPublished({ videoId: liveId }) 332 await server.live.waitUntilPublished({ videoId: liveId })
242 333
243 const video = await server.videos.getWithToken({ id: liveId }) 334 const video = videoPassword
244 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid }) 335 ? await server.videos.getWithPassword({ id: liveId, password: videoPassword })
336 : await server.videos.getWithToken({ id: liveId })
337
338 const fileToken = videoPassword
339 ? await server.videoToken.getVideoFileToken({ token: null, videoId: video.uuid, videoPassword })
340 : await server.videoToken.getVideoFileToken({ videoId: video.uuid })
245 341
246 const hls = video.streamingPlaylists[0] 342 const hls = video.streamingPlaylists[0]
247 343
@@ -253,10 +349,19 @@ describe('Object storage for video static file privacy', function () {
253 349
254 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 }) 350 await makeRawRequest({ url, token: server.accessToken, expectedStatus: HttpStatusCode.OK_200 })
255 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 }) 351 await makeRawRequest({ url, query: { videoFileToken: fileToken }, expectedStatus: HttpStatusCode.OK_200 })
256 352 if (videoPassword) {
353 await makeRawRequest({ url, headers: { 'x-peertube-video-password': videoPassword }, expectedStatus: HttpStatusCode.OK_200 })
354 }
257 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 355 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
258 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 356 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
259 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 357 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
358 if (videoPassword) {
359 await makeRawRequest({
360 url,
361 headers: { 'x-peertube-video-password': 'incorrectPassword' },
362 expectedStatus: HttpStatusCode.FORBIDDEN_403
363 })
364 }
260 } 365 }
261 366
262 await stopFfmpeg(ffmpegCommand) 367 await stopFfmpeg(ffmpegCommand)
@@ -326,6 +431,17 @@ describe('Object storage for video static file privacy', function () {
326 permanentLiveId = video.uuid 431 permanentLiveId = video.uuid
327 permanentLive = live 432 permanentLive = live
328 } 433 }
434
435 {
436 const { video, live } = await server.live.quickCreate({
437 saveReplay: false,
438 permanentLive: false,
439 privacy: VideoPrivacy.PASSWORD_PROTECTED,
440 videoPasswords: [ correctPassword ]
441 })
442 passwordProtectedLiveId = video.uuid
443 passwordProtectedLive = live
444 }
329 }) 445 })
330 446
331 it('Should create a private normal live and have a private static path', async function () { 447 it('Should create a private normal live and have a private static path', async function () {
@@ -340,6 +456,12 @@ describe('Object storage for video static file privacy', function () {
340 await checkLiveFiles(permanentLive, permanentLiveId) 456 await checkLiveFiles(permanentLive, permanentLiveId)
341 }) 457 })
342 458
459 it('Should create a password protected live and have a private static path', async function () {
460 this.timeout(240000)
461
462 await checkLiveFiles(passwordProtectedLive, passwordProtectedLiveId, correctPassword)
463 })
464
343 it('Should reinject video file token in permanent live', async function () { 465 it('Should reinject video file token in permanent live', async function () {
344 this.timeout(240000) 466 this.timeout(240000)
345 467
@@ -412,11 +534,11 @@ describe('Object storage for video static file privacy', function () {
412 534
413 it('Should not be able to access object storage proxy', async function () { 535 it('Should not be able to access object storage proxy', async function () {
414 const privateVideo = await server.videos.getWithToken({ id: videoUUID }) 536 const privateVideo = await server.videos.getWithToken({ id: videoUUID })
415 const webtorrentFilename = extractFilenameFromUrl(privateVideo.files[0].fileUrl) 537 const webVideoFilename = extractFilenameFromUrl(privateVideo.files[0].fileUrl)
416 const hlsFilename = extractFilenameFromUrl(getHLS(privateVideo).files[0].fileUrl) 538 const hlsFilename = extractFilenameFromUrl(getHLS(privateVideo).files[0].fileUrl)
417 539
418 await makeRawRequest({ 540 await makeRawRequest({
419 url: server.url + '/object-storage-proxy/webseed/private/' + webtorrentFilename, 541 url: server.url + '/object-storage-proxy/web-videos/private/' + webVideoFilename,
420 token: server.accessToken, 542 token: server.accessToken,
421 expectedStatus: HttpStatusCode.BAD_REQUEST_400 543 expectedStatus: HttpStatusCode.BAD_REQUEST_400
422 }) 544 })
diff --git a/server/tests/api/object-storage/videos.ts b/server/tests/api/object-storage/videos.ts
index f837d9966..dcc52ef06 100644
--- a/server/tests/api/object-storage/videos.ts
+++ b/server/tests/api/object-storage/videos.ts
@@ -41,8 +41,8 @@ async function checkFiles (options: {
41 playlistBucket: string 41 playlistBucket: string
42 playlistPrefix?: string 42 playlistPrefix?: string
43 43
44 webtorrentBucket: string 44 webVideoBucket: string
45 webtorrentPrefix?: string 45 webVideoPrefix?: string
46}) { 46}) {
47 const { 47 const {
48 server, 48 server,
@@ -50,20 +50,20 @@ async function checkFiles (options: {
50 originSQLCommand, 50 originSQLCommand,
51 video, 51 video,
52 playlistBucket, 52 playlistBucket,
53 webtorrentBucket, 53 webVideoBucket,
54 baseMockUrl, 54 baseMockUrl,
55 playlistPrefix, 55 playlistPrefix,
56 webtorrentPrefix 56 webVideoPrefix
57 } = options 57 } = options
58 58
59 let allFiles = video.files 59 let allFiles = video.files
60 60
61 for (const file of video.files) { 61 for (const file of video.files) {
62 const baseUrl = baseMockUrl 62 const baseUrl = baseMockUrl
63 ? `${baseMockUrl}/${webtorrentBucket}/` 63 ? `${baseMockUrl}/${webVideoBucket}/`
64 : `http://${webtorrentBucket}.${ObjectStorageCommand.getMockEndpointHost()}/` 64 : `http://${webVideoBucket}.${ObjectStorageCommand.getMockEndpointHost()}/`
65 65
66 const prefix = webtorrentPrefix || '' 66 const prefix = webVideoPrefix || ''
67 const start = baseUrl + prefix 67 const start = baseUrl + prefix
68 68
69 expectStartWith(file.fileUrl, start) 69 expectStartWith(file.fileUrl, start)
@@ -134,8 +134,8 @@ function runTestSuite (options: {
134 playlistBucket: string 134 playlistBucket: string
135 playlistPrefix?: string 135 playlistPrefix?: string
136 136
137 webtorrentBucket: string 137 webVideoBucket: string
138 webtorrentPrefix?: string 138 webVideoPrefix?: string
139 139
140 useMockBaseUrl?: boolean 140 useMockBaseUrl?: boolean
141}) { 141}) {
@@ -161,7 +161,7 @@ function runTestSuite (options: {
161 : undefined 161 : undefined
162 162
163 await objectStorage.createMockBucket(options.playlistBucket) 163 await objectStorage.createMockBucket(options.playlistBucket)
164 await objectStorage.createMockBucket(options.webtorrentBucket) 164 await objectStorage.createMockBucket(options.webVideoBucket)
165 165
166 const config = { 166 const config = {
167 object_storage: { 167 object_storage: {
@@ -181,11 +181,11 @@ function runTestSuite (options: {
181 : undefined 181 : undefined
182 }, 182 },
183 183
184 videos: { 184 web_videos: {
185 bucket_name: options.webtorrentBucket, 185 bucket_name: options.webVideoBucket,
186 prefix: options.webtorrentPrefix, 186 prefix: options.webVideoPrefix,
187 base_url: baseMockUrl 187 base_url: baseMockUrl
188 ? `${baseMockUrl}/${options.webtorrentBucket}` 188 ? `${baseMockUrl}/${options.webVideoBucket}`
189 : undefined 189 : undefined
190 } 190 }
191 } 191 }
@@ -308,7 +308,7 @@ describe('Object storage for videos', function () {
308 bucket_name: 'aaa' 308 bucket_name: 'aaa'
309 }, 309 },
310 310
311 videos: { 311 web_videos: {
312 bucket_name: 'aaa' 312 bucket_name: 'aaa'
313 } 313 }
314 } 314 }
@@ -386,27 +386,27 @@ describe('Object storage for videos', function () {
386 describe('Test simple object storage', function () { 386 describe('Test simple object storage', function () {
387 runTestSuite({ 387 runTestSuite({
388 playlistBucket: objectStorage.getMockBucketName('streaming-playlists'), 388 playlistBucket: objectStorage.getMockBucketName('streaming-playlists'),
389 webtorrentBucket: objectStorage.getMockBucketName('videos') 389 webVideoBucket: objectStorage.getMockBucketName('web-videos')
390 }) 390 })
391 }) 391 })
392 392
393 describe('Test object storage with prefix', function () { 393 describe('Test object storage with prefix', function () {
394 runTestSuite({ 394 runTestSuite({
395 playlistBucket: objectStorage.getMockBucketName('mybucket'), 395 playlistBucket: objectStorage.getMockBucketName('mybucket'),
396 webtorrentBucket: objectStorage.getMockBucketName('mybucket'), 396 webVideoBucket: objectStorage.getMockBucketName('mybucket'),
397 397
398 playlistPrefix: 'streaming-playlists_', 398 playlistPrefix: 'streaming-playlists_',
399 webtorrentPrefix: 'webtorrent_' 399 webVideoPrefix: 'webvideo_'
400 }) 400 })
401 }) 401 })
402 402
403 describe('Test object storage with prefix and base URL', function () { 403 describe('Test object storage with prefix and base URL', function () {
404 runTestSuite({ 404 runTestSuite({
405 playlistBucket: objectStorage.getMockBucketName('mybucket'), 405 playlistBucket: objectStorage.getMockBucketName('mybucket'),
406 webtorrentBucket: objectStorage.getMockBucketName('mybucket'), 406 webVideoBucket: objectStorage.getMockBucketName('mybucket'),
407 407
408 playlistPrefix: 'streaming-playlists/', 408 playlistPrefix: 'streaming-playlists/',
409 webtorrentPrefix: 'webtorrent/', 409 webVideoPrefix: 'webvideo/',
410 410
411 useMockBaseUrl: true 411 useMockBaseUrl: true
412 }) 412 })
@@ -431,7 +431,7 @@ describe('Object storage for videos', function () {
431 runTestSuite({ 431 runTestSuite({
432 maxUploadPart, 432 maxUploadPart,
433 playlistBucket: objectStorage.getMockBucketName('streaming-playlists'), 433 playlistBucket: objectStorage.getMockBucketName('streaming-playlists'),
434 webtorrentBucket: objectStorage.getMockBucketName('videos'), 434 webVideoBucket: objectStorage.getMockBucketName('web-videos'),
435 fixture 435 fixture
436 }) 436 })
437 }) 437 })
diff --git a/server/tests/api/redundancy/redundancy.ts b/server/tests/api/redundancy/redundancy.ts
index 5262c503f..0c5c27225 100644
--- a/server/tests/api/redundancy/redundancy.ts
+++ b/server/tests/api/redundancy/redundancy.ts
@@ -43,7 +43,7 @@ async function checkMagnetWebseeds (file: VideoFile, baseWebseeds: string[], ser
43 } 43 }
44} 44}
45 45
46async function createServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebtorrent = true) { 46async function createServers (strategy: VideoRedundancyStrategy | null, additionalParams: any = {}, withWebVideo = true) {
47 const strategies: any[] = [] 47 const strategies: any[] = []
48 48
49 if (strategy !== null) { 49 if (strategy !== null) {
@@ -60,8 +60,8 @@ async function createServers (strategy: VideoRedundancyStrategy | null, addition
60 60
61 const config = { 61 const config = {
62 transcoding: { 62 transcoding: {
63 webtorrent: { 63 web_videos: {
64 enabled: withWebtorrent 64 enabled: withWebVideo
65 }, 65 },
66 hls: { 66 hls: {
67 enabled: true 67 enabled: true
@@ -100,7 +100,7 @@ async function createServers (strategy: VideoRedundancyStrategy | null, addition
100} 100}
101 101
102async function ensureSameFilenames (videoUUID: string) { 102async function ensureSameFilenames (videoUUID: string) {
103 let webtorrentFilenames: string[] 103 let webVideoFilenames: string[]
104 let hlsFilenames: string[] 104 let hlsFilenames: string[]
105 105
106 for (const server of servers) { 106 for (const server of servers) {
@@ -108,24 +108,24 @@ async function ensureSameFilenames (videoUUID: string) {
108 108
109 // Ensure we use the same filenames that the origin 109 // Ensure we use the same filenames that the origin
110 110
111 const localWebtorrentFilenames = video.files.map(f => basename(f.fileUrl)).sort() 111 const localWebVideoFilenames = video.files.map(f => basename(f.fileUrl)).sort()
112 const localHLSFilenames = video.streamingPlaylists[0].files.map(f => basename(f.fileUrl)).sort() 112 const localHLSFilenames = video.streamingPlaylists[0].files.map(f => basename(f.fileUrl)).sort()
113 113
114 if (webtorrentFilenames) expect(webtorrentFilenames).to.deep.equal(localWebtorrentFilenames) 114 if (webVideoFilenames) expect(webVideoFilenames).to.deep.equal(localWebVideoFilenames)
115 else webtorrentFilenames = localWebtorrentFilenames 115 else webVideoFilenames = localWebVideoFilenames
116 116
117 if (hlsFilenames) expect(hlsFilenames).to.deep.equal(localHLSFilenames) 117 if (hlsFilenames) expect(hlsFilenames).to.deep.equal(localHLSFilenames)
118 else hlsFilenames = localHLSFilenames 118 else hlsFilenames = localHLSFilenames
119 } 119 }
120 120
121 return { webtorrentFilenames, hlsFilenames } 121 return { webVideoFilenames, hlsFilenames }
122} 122}
123 123
124async function check1WebSeed (videoUUID?: string) { 124async function check1WebSeed (videoUUID?: string) {
125 if (!videoUUID) videoUUID = video1Server2.uuid 125 if (!videoUUID) videoUUID = video1Server2.uuid
126 126
127 const webseeds = [ 127 const webseeds = [
128 `${servers[1].url}/static/webseed/` 128 `${servers[1].url}/static/web-videos/`
129 ] 129 ]
130 130
131 for (const server of servers) { 131 for (const server of servers) {
@@ -145,7 +145,7 @@ async function check2Webseeds (videoUUID?: string) {
145 145
146 const webseeds = [ 146 const webseeds = [
147 `${servers[0].url}/static/redundancy/`, 147 `${servers[0].url}/static/redundancy/`,
148 `${servers[1].url}/static/webseed/` 148 `${servers[1].url}/static/web-videos/`
149 ] 149 ]
150 150
151 for (const server of servers) { 151 for (const server of servers) {
@@ -156,11 +156,11 @@ async function check2Webseeds (videoUUID?: string) {
156 } 156 }
157 } 157 }
158 158
159 const { webtorrentFilenames } = await ensureSameFilenames(videoUUID) 159 const { webVideoFilenames } = await ensureSameFilenames(videoUUID)
160 160
161 const directories = [ 161 const directories = [
162 servers[0].getDirectoryPath('redundancy'), 162 servers[0].getDirectoryPath('redundancy'),
163 servers[1].getDirectoryPath('videos') 163 servers[1].getDirectoryPath('web-videos')
164 ] 164 ]
165 165
166 for (const directory of directories) { 166 for (const directory of directories) {
@@ -168,7 +168,7 @@ async function check2Webseeds (videoUUID?: string) {
168 expect(files).to.have.length.at.least(4) 168 expect(files).to.have.length.at.least(4)
169 169
170 // Ensure we files exist on disk 170 // Ensure we files exist on disk
171 expect(files.find(f => webtorrentFilenames.includes(f))).to.exist 171 expect(files.find(f => webVideoFilenames.includes(f))).to.exist
172 } 172 }
173} 173}
174 174
diff --git a/server/tests/api/runners/runner-studio-transcoding.ts b/server/tests/api/runners/runner-studio-transcoding.ts
index 41c556775..443a9d02a 100644
--- a/server/tests/api/runners/runner-studio-transcoding.ts
+++ b/server/tests/api/runners/runner-studio-transcoding.ts
@@ -104,7 +104,7 @@ describe('Test runner video studio transcoding', function () {
104 { 104 {
105 name: 'add-watermark' as 'add-watermark', 105 name: 'add-watermark' as 'add-watermark',
106 options: { 106 options: {
107 file: 'thumbnail.png' 107 file: 'custom-thumbnail.png'
108 } 108 }
109 }, 109 },
110 { 110 {
diff --git a/server/tests/api/runners/runner-vod-transcoding.ts b/server/tests/api/runners/runner-vod-transcoding.ts
index d9da0f40d..ca16d9c10 100644
--- a/server/tests/api/runners/runner-vod-transcoding.ts
+++ b/server/tests/api/runners/runner-vod-transcoding.ts
@@ -424,7 +424,7 @@ describe('Test runner VOD transcoding', function () {
424 424
425 await servers[0].config.enableTranscoding(true, true) 425 await servers[0].config.enableTranscoding(true, true)
426 426
427 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 427 const attributes = { name: 'audio_with_preview', previewfile: 'custom-preview.jpg', fixture: 'sample.ogg' }
428 const { uuid } = await servers[0].videos.upload({ attributes, mode: 'legacy' }) 428 const { uuid } = await servers[0].videos.upload({ attributes, mode: 'legacy' })
429 videoUUID = uuid 429 videoUUID = uuid
430 430
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 011ba268c..0e700eddb 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -46,6 +46,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
46 expect(data.cache.previews.size).to.equal(1) 46 expect(data.cache.previews.size).to.equal(1)
47 expect(data.cache.captions.size).to.equal(1) 47 expect(data.cache.captions.size).to.equal(1)
48 expect(data.cache.torrents.size).to.equal(1) 48 expect(data.cache.torrents.size).to.equal(1)
49 expect(data.cache.storyboards.size).to.equal(1)
49 50
50 expect(data.signup.enabled).to.be.true 51 expect(data.signup.enabled).to.be.true
51 expect(data.signup.limit).to.equal(4) 52 expect(data.signup.limit).to.equal(4)
@@ -78,7 +79,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
78 expect(data.transcoding.resolutions['1440p']).to.be.true 79 expect(data.transcoding.resolutions['1440p']).to.be.true
79 expect(data.transcoding.resolutions['2160p']).to.be.true 80 expect(data.transcoding.resolutions['2160p']).to.be.true
80 expect(data.transcoding.alwaysTranscodeOriginalResolution).to.be.true 81 expect(data.transcoding.alwaysTranscodeOriginalResolution).to.be.true
81 expect(data.transcoding.webtorrent.enabled).to.be.true 82 expect(data.transcoding.webVideos.enabled).to.be.true
82 expect(data.transcoding.hls.enabled).to.be.true 83 expect(data.transcoding.hls.enabled).to.be.true
83 84
84 expect(data.live.enabled).to.be.false 85 expect(data.live.enabled).to.be.false
@@ -154,6 +155,7 @@ function checkUpdatedConfig (data: CustomConfig) {
154 expect(data.cache.previews.size).to.equal(2) 155 expect(data.cache.previews.size).to.equal(2)
155 expect(data.cache.captions.size).to.equal(3) 156 expect(data.cache.captions.size).to.equal(3)
156 expect(data.cache.torrents.size).to.equal(4) 157 expect(data.cache.torrents.size).to.equal(4)
158 expect(data.cache.storyboards.size).to.equal(5)
157 159
158 expect(data.signup.enabled).to.be.false 160 expect(data.signup.enabled).to.be.false
159 expect(data.signup.limit).to.equal(5) 161 expect(data.signup.limit).to.equal(5)
@@ -190,7 +192,7 @@ function checkUpdatedConfig (data: CustomConfig) {
190 expect(data.transcoding.resolutions['2160p']).to.be.false 192 expect(data.transcoding.resolutions['2160p']).to.be.false
191 expect(data.transcoding.alwaysTranscodeOriginalResolution).to.be.false 193 expect(data.transcoding.alwaysTranscodeOriginalResolution).to.be.false
192 expect(data.transcoding.hls.enabled).to.be.false 194 expect(data.transcoding.hls.enabled).to.be.false
193 expect(data.transcoding.webtorrent.enabled).to.be.true 195 expect(data.transcoding.webVideos.enabled).to.be.true
194 196
195 expect(data.live.enabled).to.be.true 197 expect(data.live.enabled).to.be.true
196 expect(data.live.allowReplay).to.be.true 198 expect(data.live.allowReplay).to.be.true
@@ -290,6 +292,9 @@ const newCustomConfig: CustomConfig = {
290 }, 292 },
291 torrents: { 293 torrents: {
292 size: 4 294 size: 4
295 },
296 storyboards: {
297 size: 5
293 } 298 }
294 }, 299 },
295 signup: { 300 signup: {
@@ -339,7 +344,7 @@ const newCustomConfig: CustomConfig = {
339 '2160p': false 344 '2160p': false
340 }, 345 },
341 alwaysTranscodeOriginalResolution: false, 346 alwaysTranscodeOriginalResolution: false,
342 webtorrent: { 347 webVideos: {
343 enabled: true 348 enabled: true
344 }, 349 },
345 hls: { 350 hls: {
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 2a5fff82b..e3e4605ee 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -6,611 +6,636 @@ import { Video, VideoPrivacy } from '@shared/models'
6import { cleanupTests, createMultipleServers, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/server-commands' 6import { cleanupTests, createMultipleServers, PeerTubeServer, setAccessTokensToServers, waitJobs } from '@shared/server-commands'
7 7
8describe('Test follows', function () { 8describe('Test follows', function () {
9 let servers: PeerTubeServer[] = []
10 9
11 before(async function () { 10 describe('Complex follow', function () {
12 this.timeout(120000) 11 let servers: PeerTubeServer[] = []
13 12
14 servers = await createMultipleServers(3) 13 before(async function () {
14 this.timeout(120000)
15 15
16 // Get the access tokens 16 servers = await createMultipleServers(3)
17 await setAccessTokensToServers(servers)
18 })
19 17
20 describe('Data propagation after follow', function () { 18 // Get the access tokens
19 await setAccessTokensToServers(servers)
20 })
21 21
22 it('Should not have followers/followings', async function () { 22 describe('Data propagation after follow', function () {
23 for (const server of servers) {
24 const bodies = await Promise.all([
25 server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' }),
26 server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
27 ])
28 23
29 for (const body of bodies) { 24 it('Should not have followers/followings', async function () {
30 expect(body.total).to.equal(0) 25 for (const server of servers) {
26 const bodies = await Promise.all([
27 server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' }),
28 server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
29 ])
31 30
32 const follows = body.data 31 for (const body of bodies) {
33 expect(follows).to.be.an('array') 32 expect(body.total).to.equal(0)
34 expect(follows).to.have.lengthOf(0) 33
34 const follows = body.data
35 expect(follows).to.be.an('array')
36 expect(follows).to.have.lengthOf(0)
37 }
35 } 38 }
36 } 39 })
37 }) 40
41 it('Should have server 1 following root account of server 2 and server 3', async function () {
42 this.timeout(30000)
38 43
39 it('Should have server 1 following root account of server 2 and server 3', async function () { 44 await servers[0].follows.follow({
40 this.timeout(30000) 45 hosts: [ servers[2].url ],
46 handles: [ 'root@' + servers[1].host ]
47 })
41 48
42 await servers[0].follows.follow({ 49 await waitJobs(servers)
43 hosts: [ servers[2].url ],
44 handles: [ 'root@' + servers[1].host ]
45 }) 50 })
46 51
47 await waitJobs(servers) 52 it('Should have 2 followings on server 1', async function () {
48 }) 53 const body = await servers[0].follows.getFollowings({ start: 0, count: 1, sort: 'createdAt' })
54 expect(body.total).to.equal(2)
49 55
50 it('Should have 2 followings on server 1', async function () { 56 let follows = body.data
51 const body = await servers[0].follows.getFollowings({ start: 0, count: 1, sort: 'createdAt' }) 57 expect(follows).to.be.an('array')
52 expect(body.total).to.equal(2) 58 expect(follows).to.have.lengthOf(1)
53 59
54 let follows = body.data 60 const body2 = await servers[0].follows.getFollowings({ start: 1, count: 1, sort: 'createdAt' })
55 expect(follows).to.be.an('array') 61 follows = follows.concat(body2.data)
56 expect(follows).to.have.lengthOf(1)
57 62
58 const body2 = await servers[0].follows.getFollowings({ start: 1, count: 1, sort: 'createdAt' }) 63 const server2Follow = follows.find(f => f.following.host === servers[1].host)
59 follows = follows.concat(body2.data) 64 const server3Follow = follows.find(f => f.following.host === servers[2].host)
60 65
61 const server2Follow = follows.find(f => f.following.host === servers[1].host) 66 expect(server2Follow).to.not.be.undefined
62 const server3Follow = follows.find(f => f.following.host === servers[2].host) 67 expect(server2Follow.following.name).to.equal('root')
68 expect(server2Follow.state).to.equal('accepted')
63 69
64 expect(server2Follow).to.not.be.undefined 70 expect(server3Follow).to.not.be.undefined
65 expect(server2Follow.following.name).to.equal('root') 71 expect(server3Follow.following.name).to.equal('peertube')
66 expect(server2Follow.state).to.equal('accepted') 72 expect(server3Follow.state).to.equal('accepted')
73 })
67 74
68 expect(server3Follow).to.not.be.undefined 75 it('Should have 0 followings on server 2 and 3', async function () {
69 expect(server3Follow.following.name).to.equal('peertube') 76 for (const server of [ servers[1], servers[2] ]) {
70 expect(server3Follow.state).to.equal('accepted') 77 const body = await server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' })
71 }) 78 expect(body.total).to.equal(0)
72 79
73 it('Should have 0 followings on server 2 and 3', async function () { 80 const follows = body.data
74 for (const server of [ servers[1], servers[2] ]) { 81 expect(follows).to.be.an('array')
75 const body = await server.follows.getFollowings({ start: 0, count: 5, sort: 'createdAt' }) 82 expect(follows).to.have.lengthOf(0)
76 expect(body.total).to.equal(0) 83 }
84 })
85
86 it('Should have 1 followers on server 3', async function () {
87 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' })
88 expect(body.total).to.equal(1)
77 89
78 const follows = body.data 90 const follows = body.data
79 expect(follows).to.be.an('array') 91 expect(follows).to.be.an('array')
80 expect(follows).to.have.lengthOf(0) 92 expect(follows).to.have.lengthOf(1)
81 } 93 expect(follows[0].follower.host).to.equal(servers[0].host)
82 }) 94 })
83 95
84 it('Should have 1 followers on server 3', async function () { 96 it('Should have 0 followers on server 1 and 2', async function () {
85 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' }) 97 for (const server of [ servers[0], servers[1] ]) {
86 expect(body.total).to.equal(1) 98 const body = await server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' })
99 expect(body.total).to.equal(0)
87 100
88 const follows = body.data 101 const follows = body.data
89 expect(follows).to.be.an('array') 102 expect(follows).to.be.an('array')
90 expect(follows).to.have.lengthOf(1) 103 expect(follows).to.have.lengthOf(0)
91 expect(follows[0].follower.host).to.equal(servers[0].host) 104 }
92 }) 105 })
93 106
94 it('Should have 0 followers on server 1 and 2', async function () { 107 it('Should search/filter followings on server 1', async function () {
95 for (const server of [ servers[0], servers[1] ]) { 108 const sort = 'createdAt'
96 const body = await server.follows.getFollowers({ start: 0, count: 5, sort: 'createdAt' }) 109 const start = 0
97 expect(body.total).to.equal(0) 110 const count = 1
98 111
99 const follows = body.data 112 {
100 expect(follows).to.be.an('array') 113 const search = ':' + servers[1].port
101 expect(follows).to.have.lengthOf(0)
102 }
103 })
104 114
105 it('Should search/filter followings on server 1', async function () { 115 {
106 const sort = 'createdAt' 116 const body = await servers[0].follows.getFollowings({ start, count, sort, search })
107 const start = 0 117 expect(body.total).to.equal(1)
108 const count = 1
109 118
110 { 119 const follows = body.data
111 const search = ':' + servers[1].port 120 expect(follows).to.have.lengthOf(1)
121 expect(follows[0].following.host).to.equal(servers[1].host)
122 }
112 123
113 { 124 {
114 const body = await servers[0].follows.getFollowings({ start, count, sort, search }) 125 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted' })
115 expect(body.total).to.equal(1) 126 expect(body.total).to.equal(1)
127 expect(body.data).to.have.lengthOf(1)
128 }
116 129
117 const follows = body.data 130 {
118 expect(follows).to.have.lengthOf(1) 131 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted', actorType: 'Person' })
119 expect(follows[0].following.host).to.equal(servers[1].host) 132 expect(body.total).to.equal(1)
120 } 133 expect(body.data).to.have.lengthOf(1)
134 }
121 135
122 { 136 {
123 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted' }) 137 const body = await servers[0].follows.getFollowings({
124 expect(body.total).to.equal(1) 138 start,
125 expect(body.data).to.have.lengthOf(1) 139 count,
140 sort,
141 search,
142 state: 'accepted',
143 actorType: 'Application'
144 })
145 expect(body.total).to.equal(0)
146 expect(body.data).to.have.lengthOf(0)
147 }
148
149 {
150 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'pending' })
151 expect(body.total).to.equal(0)
152 expect(body.data).to.have.lengthOf(0)
153 }
126 } 154 }
127 155
128 { 156 {
129 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'accepted', actorType: 'Person' }) 157 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'root' })
130 expect(body.total).to.equal(1) 158 expect(body.total).to.equal(1)
131 expect(body.data).to.have.lengthOf(1) 159 expect(body.data).to.have.lengthOf(1)
132 } 160 }
133 161
134 { 162 {
135 const body = await servers[0].follows.getFollowings({ 163 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'bla' })
136 start,
137 count,
138 sort,
139 search,
140 state: 'accepted',
141 actorType: 'Application'
142 })
143 expect(body.total).to.equal(0) 164 expect(body.total).to.equal(0)
144 expect(body.data).to.have.lengthOf(0)
145 }
146 165
147 {
148 const body = await servers[0].follows.getFollowings({ start, count, sort, search, state: 'pending' })
149 expect(body.total).to.equal(0)
150 expect(body.data).to.have.lengthOf(0) 166 expect(body.data).to.have.lengthOf(0)
151 } 167 }
152 } 168 })
153 169
154 { 170 it('Should search/filter followers on server 2', async function () {
155 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'root' }) 171 const start = 0
156 expect(body.total).to.equal(1) 172 const count = 5
157 expect(body.data).to.have.lengthOf(1) 173 const sort = 'createdAt'
158 }
159 174
160 { 175 {
161 const body = await servers[0].follows.getFollowings({ start, count, sort, search: 'bla' }) 176 const search = servers[0].port + ''
162 expect(body.total).to.equal(0)
163 177
164 expect(body.data).to.have.lengthOf(0) 178 {
165 } 179 const body = await servers[2].follows.getFollowers({ start, count, sort, search })
166 }) 180 expect(body.total).to.equal(1)
167 181
168 it('Should search/filter followers on server 2', async function () { 182 const follows = body.data
169 const start = 0 183 expect(follows).to.have.lengthOf(1)
170 const count = 5 184 expect(follows[0].following.host).to.equal(servers[2].host)
171 const sort = 'createdAt' 185 }
172 186
173 { 187 {
174 const search = servers[0].port + '' 188 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted' })
189 expect(body.total).to.equal(1)
190 expect(body.data).to.have.lengthOf(1)
191 }
175 192
176 { 193 {
177 const body = await servers[2].follows.getFollowers({ start, count, sort, search }) 194 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted', actorType: 'Person' })
178 expect(body.total).to.equal(1) 195 expect(body.total).to.equal(0)
196 expect(body.data).to.have.lengthOf(0)
197 }
179 198
180 const follows = body.data 199 {
181 expect(follows).to.have.lengthOf(1) 200 const body = await servers[2].follows.getFollowers({
182 expect(follows[0].following.host).to.equal(servers[2].host) 201 start,
183 } 202 count,
203 sort,
204 search,
205 state: 'accepted',
206 actorType: 'Application'
207 })
208 expect(body.total).to.equal(1)
209 expect(body.data).to.have.lengthOf(1)
210 }
184 211
185 { 212 {
186 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted' }) 213 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'pending' })
187 expect(body.total).to.equal(1) 214 expect(body.total).to.equal(0)
188 expect(body.data).to.have.lengthOf(1) 215 expect(body.data).to.have.lengthOf(0)
216 }
189 } 217 }
190 218
191 { 219 {
192 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'accepted', actorType: 'Person' }) 220 const body = await servers[2].follows.getFollowers({ start, count, sort, search: 'bla' })
193 expect(body.total).to.equal(0) 221 expect(body.total).to.equal(0)
194 expect(body.data).to.have.lengthOf(0)
195 }
196 222
197 { 223 const follows = body.data
198 const body = await servers[2].follows.getFollowers({ 224 expect(follows).to.have.lengthOf(0)
199 start,
200 count,
201 sort,
202 search,
203 state: 'accepted',
204 actorType: 'Application'
205 })
206 expect(body.total).to.equal(1)
207 expect(body.data).to.have.lengthOf(1)
208 } 225 }
226 })
209 227
210 { 228 it('Should have the correct follows counts', async function () {
211 const body = await servers[2].follows.getFollowers({ start, count, sort, search, state: 'pending' }) 229 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 })
212 expect(body.total).to.equal(0) 230 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
213 expect(body.data).to.have.lengthOf(0) 231 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
214 } 232
215 } 233 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh)
234 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
235 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
236 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
237
238 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
239 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
240 })
241
242 it('Should unfollow server 3 on server 1', async function () {
243 this.timeout(15000)
244
245 await servers[0].follows.unfollow({ target: servers[2] })
216 246
217 { 247 await waitJobs(servers)
218 const body = await servers[2].follows.getFollowers({ start, count, sort, search: 'bla' }) 248 })
249
250 it('Should not follow server 3 on server 1 anymore', async function () {
251 const body = await servers[0].follows.getFollowings({ start: 0, count: 2, sort: 'createdAt' })
252 expect(body.total).to.equal(1)
253
254 const follows = body.data
255 expect(follows).to.be.an('array')
256 expect(follows).to.have.lengthOf(1)
257
258 expect(follows[0].following.host).to.equal(servers[1].host)
259 })
260
261 it('Should not have server 1 as follower on server 3 anymore', async function () {
262 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' })
219 expect(body.total).to.equal(0) 263 expect(body.total).to.equal(0)
220 264
221 const follows = body.data 265 const follows = body.data
266 expect(follows).to.be.an('array')
222 expect(follows).to.have.lengthOf(0) 267 expect(follows).to.have.lengthOf(0)
223 } 268 })
224 })
225 269
226 it('Should have the correct follows counts', async function () { 270 it('Should have the correct follows counts after the unfollow', async function () {
227 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 }) 271 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
228 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 }) 272 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
229 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 }) 273 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
230 274
231 // Server 2 and 3 does not know server 1 follow another server (there was not a refresh) 275 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
232 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 }) 276 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
233 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 }) 277 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
234 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
235 278
236 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 }) 279 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 0 })
237 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 }) 280 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
238 }) 281 })
239 282
240 it('Should unfollow server 3 on server 1', async function () { 283 it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () {
241 this.timeout(15000) 284 this.timeout(160000)
242 285
243 await servers[0].follows.unfollow({ target: servers[2] }) 286 await servers[1].videos.upload({ attributes: { name: 'server2' } })
287 await servers[2].videos.upload({ attributes: { name: 'server3' } })
244 288
245 await waitJobs(servers) 289 await waitJobs(servers)
246 })
247 290
248 it('Should not follow server 3 on server 1 anymore', async function () { 291 {
249 const body = await servers[0].follows.getFollowings({ start: 0, count: 2, sort: 'createdAt' }) 292 const { total, data } = await servers[0].videos.list()
250 expect(body.total).to.equal(1) 293 expect(total).to.equal(1)
294 expect(data[0].name).to.equal('server2')
295 }
251 296
252 const follows = body.data 297 {
253 expect(follows).to.be.an('array') 298 const { total, data } = await servers[1].videos.list()
254 expect(follows).to.have.lengthOf(1) 299 expect(total).to.equal(1)
300 expect(data[0].name).to.equal('server2')
301 }
255 302
256 expect(follows[0].following.host).to.equal(servers[1].host) 303 {
257 }) 304 const { total, data } = await servers[2].videos.list()
305 expect(total).to.equal(1)
306 expect(data[0].name).to.equal('server3')
307 }
308 })
258 309
259 it('Should not have server 1 as follower on server 3 anymore', async function () { 310 it('Should remove account follow', async function () {
260 const body = await servers[2].follows.getFollowers({ start: 0, count: 1, sort: 'createdAt' }) 311 this.timeout(15000)
261 expect(body.total).to.equal(0)
262 312
263 const follows = body.data 313 await servers[0].follows.unfollow({ target: 'root@' + servers[1].host })
264 expect(follows).to.be.an('array')
265 expect(follows).to.have.lengthOf(0)
266 })
267 314
268 it('Should have the correct follows counts after the unfollow', async function () { 315 await waitJobs(servers)
269 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 }) 316 })
270 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 1, following: 0 })
271 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 })
272 317
273 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 }) 318 it('Should have removed the account follow', async function () {
274 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 1, following: 0 }) 319 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
275 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 }) 320 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
276 321
277 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 0 }) 322 {
278 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 0, following: 0 }) 323 const { total, data } = await servers[0].follows.getFollowings()
279 }) 324 expect(total).to.equal(0)
325 expect(data).to.have.lengthOf(0)
326 }
280 327
281 it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () { 328 {
282 this.timeout(160000) 329 const { total, data } = await servers[0].videos.list()
330 expect(total).to.equal(0)
331 expect(data).to.have.lengthOf(0)
332 }
333 })
283 334
284 await servers[1].videos.upload({ attributes: { name: 'server2' } }) 335 it('Should follow a channel', async function () {
285 await servers[2].videos.upload({ attributes: { name: 'server3' } }) 336 this.timeout(15000)
286 337
287 await waitJobs(servers) 338 await servers[0].follows.follow({
339 handles: [ 'root_channel@' + servers[1].host ]
340 })
288 341
289 { 342 await waitJobs(servers)
290 const { total, data } = await servers[0].videos.list()
291 expect(total).to.equal(1)
292 expect(data[0].name).to.equal('server2')
293 }
294 343
295 { 344 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
296 const { total, data } = await servers[1].videos.list() 345 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
297 expect(total).to.equal(1)
298 expect(data[0].name).to.equal('server2')
299 }
300 346
301 { 347 {
302 const { total, data } = await servers[2].videos.list() 348 const { total, data } = await servers[0].follows.getFollowings()
303 expect(total).to.equal(1) 349 expect(total).to.equal(1)
304 expect(data[0].name).to.equal('server3') 350 expect(data).to.have.lengthOf(1)
305 } 351 }
352
353 {
354 const { total, data } = await servers[0].videos.list()
355 expect(total).to.equal(1)
356 expect(data).to.have.lengthOf(1)
357 }
358 })
306 }) 359 })
307 360
308 it('Should remove account follow', async function () { 361 describe('Should propagate data on a new server follow', function () {
309 this.timeout(15000) 362 let video4: Video
310 363
311 await servers[0].follows.unfollow({ target: 'root@' + servers[1].host }) 364 before(async function () {
365 this.timeout(240000)
312 366
313 await waitJobs(servers) 367 const video4Attributes = {
314 }) 368 name: 'server3-4',
369 category: 2,
370 nsfw: true,
371 licence: 6,
372 tags: [ 'tag1', 'tag2', 'tag3' ]
373 }
315 374
316 it('Should have removed the account follow', async function () { 375 await servers[2].videos.upload({ attributes: { name: 'server3-2' } })
317 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 }) 376 await servers[2].videos.upload({ attributes: { name: 'server3-3' } })
318 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
319 377
320 { 378 const video4CreateResult = await servers[2].videos.upload({ attributes: video4Attributes })
321 const { total, data } = await servers[0].follows.getFollowings()
322 expect(total).to.equal(0)
323 expect(data).to.have.lengthOf(0)
324 }
325 379
326 { 380 await servers[2].videos.upload({ attributes: { name: 'server3-5' } })
327 const { total, data } = await servers[0].videos.list() 381 await servers[2].videos.upload({ attributes: { name: 'server3-6' } })
328 expect(total).to.equal(0)
329 expect(data).to.have.lengthOf(0)
330 }
331 })
332 382
333 it('Should follow a channel', async function () { 383 {
334 this.timeout(15000) 384 const userAccessToken = await servers[2].users.generateUserAndToken('captain')
335 385
336 await servers[0].follows.follow({ 386 await servers[2].videos.rate({ id: video4CreateResult.id, rating: 'like' })
337 handles: [ 'root_channel@' + servers[1].host ] 387 await servers[2].videos.rate({ token: userAccessToken, id: video4CreateResult.id, rating: 'dislike' })
338 }) 388 }
339 389
340 await waitJobs(servers) 390 {
391 await servers[2].comments.createThread({ videoId: video4CreateResult.id, text: 'my super first comment' })
341 392
342 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 }) 393 await servers[2].comments.addReplyToLastThread({ text: 'my super answer to thread 1' })
343 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 }) 394 await servers[2].comments.addReplyToLastReply({ text: 'my super answer to answer of thread 1' })
395 await servers[2].comments.addReplyToLastThread({ text: 'my second answer to thread 1' })
396 }
344 397
345 { 398 {
346 const { total, data } = await servers[0].follows.getFollowings() 399 const { id: threadId } = await servers[2].comments.createThread({ videoId: video4CreateResult.id, text: 'will be deleted' })
347 expect(total).to.equal(1) 400 await servers[2].comments.addReplyToLastThread({ text: 'answer to deleted' })
348 expect(data).to.have.lengthOf(1)
349 }
350 401
351 { 402 const { id: replyId } = await servers[2].comments.addReplyToLastThread({ text: 'will also be deleted' })
352 const { total, data } = await servers[0].videos.list()
353 expect(total).to.equal(1)
354 expect(data).to.have.lengthOf(1)
355 }
356 })
357 })
358 403
359 describe('Should propagate data on a new server follow', function () { 404 await servers[2].comments.addReplyToLastReply({ text: 'my second answer to deleted' })
360 let video4: Video
361 405
362 before(async function () { 406 await servers[2].comments.delete({ videoId: video4CreateResult.id, commentId: threadId })
363 this.timeout(120000) 407 await servers[2].comments.delete({ videoId: video4CreateResult.id, commentId: replyId })
408 }
364 409
365 const video4Attributes = { 410 await servers[2].captions.add({
366 name: 'server3-4', 411 language: 'ar',
367 category: 2, 412 videoId: video4CreateResult.id,
368 nsfw: true, 413 fixture: 'subtitle-good2.vtt'
369 licence: 6, 414 })
370 tags: [ 'tag1', 'tag2', 'tag3' ]
371 }
372 415
373 await servers[2].videos.upload({ attributes: { name: 'server3-2' } }) 416 await waitJobs(servers)
374 await servers[2].videos.upload({ attributes: { name: 'server3-3' } })
375 const video4CreateResult = await servers[2].videos.upload({ attributes: video4Attributes })
376 await servers[2].videos.upload({ attributes: { name: 'server3-5' } })
377 await servers[2].videos.upload({ attributes: { name: 'server3-6' } })
378 417
379 { 418 // Server 1 follows server 3
380 const userAccessToken = await servers[2].users.generateUserAndToken('captain') 419 await servers[0].follows.follow({ hosts: [ servers[2].url ] })
381 420
382 await servers[2].videos.rate({ id: video4CreateResult.id, rating: 'like' }) 421 await waitJobs(servers)
383 await servers[2].videos.rate({ token: userAccessToken, id: video4CreateResult.id, rating: 'dislike' }) 422 })
384 }
385 423
386 { 424 it('Should have the correct follows counts', async function () {
387 await servers[2].comments.createThread({ videoId: video4CreateResult.id, text: 'my super first comment' }) 425 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 })
426 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
427 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
428 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
388 429
389 await servers[2].comments.addReplyToLastThread({ text: 'my super answer to thread 1' }) 430 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
390 await servers[2].comments.addReplyToLastReply({ text: 'my super answer to answer of thread 1' }) 431 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
391 await servers[2].comments.addReplyToLastThread({ text: 'my second answer to thread 1' }) 432 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
392 } 433 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
393 434
394 { 435 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 })
395 const { id: threadId } = await servers[2].comments.createThread({ videoId: video4CreateResult.id, text: 'will be deleted' }) 436 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
396 await servers[2].comments.addReplyToLastThread({ text: 'answer to deleted' }) 437 })
397 438
398 const { id: replyId } = await servers[2].comments.addReplyToLastThread({ text: 'will also be deleted' }) 439 it('Should have propagated videos', async function () {
440 const { total, data } = await servers[0].videos.list()
441 expect(total).to.equal(7)
442
443 const video2 = data.find(v => v.name === 'server3-2')
444 video4 = data.find(v => v.name === 'server3-4')
445 const video6 = data.find(v => v.name === 'server3-6')
446
447 expect(video2).to.not.be.undefined
448 expect(video4).to.not.be.undefined
449 expect(video6).to.not.be.undefined
450
451 const isLocal = false
452 const checkAttributes = {
453 name: 'server3-4',
454 category: 2,
455 licence: 6,
456 language: 'zh',
457 nsfw: true,
458 description: 'my super description',
459 support: 'my super support text',
460 account: {
461 name: 'root',
462 host: servers[2].host
463 },
464 isLocal,
465 commentsEnabled: true,
466 downloadEnabled: true,
467 duration: 5,
468 tags: [ 'tag1', 'tag2', 'tag3' ],
469 privacy: VideoPrivacy.PUBLIC,
470 likes: 1,
471 dislikes: 1,
472 channel: {
473 displayName: 'Main root channel',
474 name: 'root_channel',
475 description: '',
476 isLocal
477 },
478 fixture: 'video_short.webm',
479 files: [
480 {
481 resolution: 720,
482 size: 218910
483 }
484 ]
485 }
486 await completeVideoCheck({
487 server: servers[0],
488 originServer: servers[2],
489 videoUUID: video4.uuid,
490 attributes: checkAttributes
491 })
492 })
399 493
400 await servers[2].comments.addReplyToLastReply({ text: 'my second answer to deleted' }) 494 it('Should have propagated comments', async function () {
495 const { total, data } = await servers[0].comments.listThreads({ videoId: video4.id, sort: 'createdAt' })
401 496
402 await servers[2].comments.delete({ videoId: video4CreateResult.id, commentId: threadId }) 497 expect(total).to.equal(2)
403 await servers[2].comments.delete({ videoId: video4CreateResult.id, commentId: replyId }) 498 expect(data).to.be.an('array')
404 } 499 expect(data).to.have.lengthOf(2)
405 500
406 await servers[2].captions.add({ 501 {
407 language: 'ar', 502 const comment = data[0]
408 videoId: video4CreateResult.id, 503 expect(comment.inReplyToCommentId).to.be.null
409 fixture: 'subtitle-good2.vtt' 504 expect(comment.text).equal('my super first comment')
410 }) 505 expect(comment.videoId).to.equal(video4.id)
506 expect(comment.id).to.equal(comment.threadId)
507 expect(comment.account.name).to.equal('root')
508 expect(comment.account.host).to.equal(servers[2].host)
509 expect(comment.totalReplies).to.equal(3)
510 expect(dateIsValid(comment.createdAt as string)).to.be.true
511 expect(dateIsValid(comment.updatedAt as string)).to.be.true
512
513 const threadId = comment.threadId
514
515 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId })
516 expect(tree.comment.text).equal('my super first comment')
517 expect(tree.children).to.have.lengthOf(2)
518
519 const firstChild = tree.children[0]
520 expect(firstChild.comment.text).to.equal('my super answer to thread 1')
521 expect(firstChild.children).to.have.lengthOf(1)
522
523 const childOfFirstChild = firstChild.children[0]
524 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
525 expect(childOfFirstChild.children).to.have.lengthOf(0)
526
527 const secondChild = tree.children[1]
528 expect(secondChild.comment.text).to.equal('my second answer to thread 1')
529 expect(secondChild.children).to.have.lengthOf(0)
530 }
411 531
412 await waitJobs(servers) 532 {
533 const deletedComment = data[1]
534 expect(deletedComment).to.not.be.undefined
535 expect(deletedComment.isDeleted).to.be.true
536 expect(deletedComment.deletedAt).to.not.be.null
537 expect(deletedComment.text).to.equal('')
538 expect(deletedComment.inReplyToCommentId).to.be.null
539 expect(deletedComment.account).to.be.null
540 expect(deletedComment.totalReplies).to.equal(2)
541 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
542
543 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId: deletedComment.threadId })
544 const [ commentRoot, deletedChildRoot ] = tree.children
545
546 expect(deletedChildRoot).to.not.be.undefined
547 expect(deletedChildRoot.comment.isDeleted).to.be.true
548 expect(deletedChildRoot.comment.deletedAt).to.not.be.null
549 expect(deletedChildRoot.comment.text).to.equal('')
550 expect(deletedChildRoot.comment.inReplyToCommentId).to.equal(deletedComment.id)
551 expect(deletedChildRoot.comment.account).to.be.null
552 expect(deletedChildRoot.children).to.have.lengthOf(1)
553
554 const answerToDeletedChild = deletedChildRoot.children[0]
555 expect(answerToDeletedChild.comment).to.not.be.undefined
556 expect(answerToDeletedChild.comment.inReplyToCommentId).to.equal(deletedChildRoot.comment.id)
557 expect(answerToDeletedChild.comment.text).to.equal('my second answer to deleted')
558 expect(answerToDeletedChild.comment.account.name).to.equal('root')
559
560 expect(commentRoot.comment).to.not.be.undefined
561 expect(commentRoot.comment.inReplyToCommentId).to.equal(deletedComment.id)
562 expect(commentRoot.comment.text).to.equal('answer to deleted')
563 expect(commentRoot.comment.account.name).to.equal('root')
564 }
565 })
413 566
414 // Server 1 follows server 3 567 it('Should have propagated captions', async function () {
415 await servers[0].follows.follow({ hosts: [ servers[2].url ] }) 568 const body = await servers[0].captions.list({ videoId: video4.id })
569 expect(body.total).to.equal(1)
570 expect(body.data).to.have.lengthOf(1)
416 571
417 await waitJobs(servers) 572 const caption1 = body.data[0]
418 }) 573 expect(caption1.language.id).to.equal('ar')
574 expect(caption1.language.label).to.equal('Arabic')
575 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/.+-ar.vtt$'))
576 await testCaptionFile(servers[0].url, caption1.captionPath, 'Subtitle good 2.')
577 })
419 578
420 it('Should have the correct follows counts', async function () { 579 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () {
421 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[0].host, followers: 0, following: 2 }) 580 this.timeout(5000)
422 await expectAccountFollows({ server: servers[0], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
423 await expectChannelsFollows({ server: servers[0], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
424 await expectAccountFollows({ server: servers[0], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
425 581
426 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 }) 582 await servers[0].follows.unfollow({ target: servers[2] })
427 await expectAccountFollows({ server: servers[1], handle: 'peertube@' + servers[1].host, followers: 0, following: 0 })
428 await expectAccountFollows({ server: servers[1], handle: 'root@' + servers[1].host, followers: 0, following: 0 })
429 await expectChannelsFollows({ server: servers[1], handle: 'root_channel@' + servers[1].host, followers: 1, following: 0 })
430 583
431 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[0].host, followers: 0, following: 1 }) 584 await waitJobs(servers)
432 await expectAccountFollows({ server: servers[2], handle: 'peertube@' + servers[2].host, followers: 1, following: 0 })
433 })
434 585
435 it('Should have propagated videos', async function () { 586 const { total } = await servers[0].videos.list()
436 const { total, data } = await servers[0].videos.list() 587 expect(total).to.equal(1)
437 expect(total).to.equal(7)
438
439 const video2 = data.find(v => v.name === 'server3-2')
440 video4 = data.find(v => v.name === 'server3-4')
441 const video6 = data.find(v => v.name === 'server3-6')
442
443 expect(video2).to.not.be.undefined
444 expect(video4).to.not.be.undefined
445 expect(video6).to.not.be.undefined
446
447 const isLocal = false
448 const checkAttributes = {
449 name: 'server3-4',
450 category: 2,
451 licence: 6,
452 language: 'zh',
453 nsfw: true,
454 description: 'my super description',
455 support: 'my super support text',
456 account: {
457 name: 'root',
458 host: servers[2].host
459 },
460 isLocal,
461 commentsEnabled: true,
462 downloadEnabled: true,
463 duration: 5,
464 tags: [ 'tag1', 'tag2', 'tag3' ],
465 privacy: VideoPrivacy.PUBLIC,
466 likes: 1,
467 dislikes: 1,
468 channel: {
469 displayName: 'Main root channel',
470 name: 'root_channel',
471 description: '',
472 isLocal
473 },
474 fixture: 'video_short.webm',
475 files: [
476 {
477 resolution: 720,
478 size: 218910
479 }
480 ]
481 }
482 await completeVideoCheck({
483 server: servers[0],
484 originServer: servers[2],
485 videoUUID: video4.uuid,
486 attributes: checkAttributes
487 }) 588 })
488 }) 589 })
489 590
490 it('Should have propagated comments', async function () { 591 after(async function () {
491 const { total, data } = await servers[0].comments.listThreads({ videoId: video4.id, sort: 'createdAt' }) 592 await cleanupTests(servers)
492
493 expect(total).to.equal(2)
494 expect(data).to.be.an('array')
495 expect(data).to.have.lengthOf(2)
496
497 {
498 const comment = data[0]
499 expect(comment.inReplyToCommentId).to.be.null
500 expect(comment.text).equal('my super first comment')
501 expect(comment.videoId).to.equal(video4.id)
502 expect(comment.id).to.equal(comment.threadId)
503 expect(comment.account.name).to.equal('root')
504 expect(comment.account.host).to.equal(servers[2].host)
505 expect(comment.totalReplies).to.equal(3)
506 expect(dateIsValid(comment.createdAt as string)).to.be.true
507 expect(dateIsValid(comment.updatedAt as string)).to.be.true
508
509 const threadId = comment.threadId
510
511 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId })
512 expect(tree.comment.text).equal('my super first comment')
513 expect(tree.children).to.have.lengthOf(2)
514
515 const firstChild = tree.children[0]
516 expect(firstChild.comment.text).to.equal('my super answer to thread 1')
517 expect(firstChild.children).to.have.lengthOf(1)
518
519 const childOfFirstChild = firstChild.children[0]
520 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
521 expect(childOfFirstChild.children).to.have.lengthOf(0)
522
523 const secondChild = tree.children[1]
524 expect(secondChild.comment.text).to.equal('my second answer to thread 1')
525 expect(secondChild.children).to.have.lengthOf(0)
526 }
527
528 {
529 const deletedComment = data[1]
530 expect(deletedComment).to.not.be.undefined
531 expect(deletedComment.isDeleted).to.be.true
532 expect(deletedComment.deletedAt).to.not.be.null
533 expect(deletedComment.text).to.equal('')
534 expect(deletedComment.inReplyToCommentId).to.be.null
535 expect(deletedComment.account).to.be.null
536 expect(deletedComment.totalReplies).to.equal(2)
537 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
538
539 const tree = await servers[0].comments.getThread({ videoId: video4.id, threadId: deletedComment.threadId })
540 const [ commentRoot, deletedChildRoot ] = tree.children
541
542 expect(deletedChildRoot).to.not.be.undefined
543 expect(deletedChildRoot.comment.isDeleted).to.be.true
544 expect(deletedChildRoot.comment.deletedAt).to.not.be.null
545 expect(deletedChildRoot.comment.text).to.equal('')
546 expect(deletedChildRoot.comment.inReplyToCommentId).to.equal(deletedComment.id)
547 expect(deletedChildRoot.comment.account).to.be.null
548 expect(deletedChildRoot.children).to.have.lengthOf(1)
549
550 const answerToDeletedChild = deletedChildRoot.children[0]
551 expect(answerToDeletedChild.comment).to.not.be.undefined
552 expect(answerToDeletedChild.comment.inReplyToCommentId).to.equal(deletedChildRoot.comment.id)
553 expect(answerToDeletedChild.comment.text).to.equal('my second answer to deleted')
554 expect(answerToDeletedChild.comment.account.name).to.equal('root')
555
556 expect(commentRoot.comment).to.not.be.undefined
557 expect(commentRoot.comment.inReplyToCommentId).to.equal(deletedComment.id)
558 expect(commentRoot.comment.text).to.equal('answer to deleted')
559 expect(commentRoot.comment.account.name).to.equal('root')
560 }
561 }) 593 })
594 })
562 595
563 it('Should have propagated captions', async function () { 596 describe('Simple data propagation propagate data on a new channel follow', function () {
564 const body = await servers[0].captions.list({ videoId: video4.id }) 597 let servers: PeerTubeServer[] = []
565 expect(body.total).to.equal(1)
566 expect(body.data).to.have.lengthOf(1)
567 598
568 const caption1 = body.data[0] 599 before(async function () {
569 expect(caption1.language.id).to.equal('ar') 600 this.timeout(120000)
570 expect(caption1.language.label).to.equal('Arabic')
571 expect(caption1.captionPath).to.match(new RegExp('^/lazy-static/video-captions/.+-ar.vtt$'))
572 await testCaptionFile(servers[0].url, caption1.captionPath, 'Subtitle good 2.')
573 })
574 601
575 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () { 602 servers = await createMultipleServers(3)
576 this.timeout(5000) 603 await setAccessTokensToServers(servers)
577 604
578 await servers[0].follows.unfollow({ target: servers[2] }) 605 await servers[0].videos.upload({ attributes: { name: 'video to add' } })
579 606
580 await waitJobs(servers) 607 await waitJobs(servers)
581 608
582 const { total } = await servers[0].videos.list() 609 for (const server of [ servers[1], servers[2] ]) {
583 expect(total).to.equal(1) 610 const video = await server.videos.find({ name: 'video to add' })
611 expect(video).to.not.exist
612 }
584 }) 613 })
585 })
586
587 describe('Should propagate data on a new channel follow', function () {
588 614
589 before(async function () { 615 it('Should have propagated video after new channel follow', async function () {
590 this.timeout(60000) 616 this.timeout(60000)
591 617
592 await servers[2].videos.upload({ attributes: { name: 'server3-7' } }) 618 await servers[1].follows.follow({ handles: [ 'root_channel@' + servers[0].host ] })
593 619
594 await waitJobs(servers) 620 await waitJobs(servers)
595 621
596 const video = await servers[0].videos.find({ name: 'server3-7' }) 622 const video = await servers[1].videos.find({ name: 'video to add' })
597 expect(video).to.not.exist 623 expect(video).to.exist
598 }) 624 })
599 625
600 it('Should have propagated channel video', async function () { 626 it('Should have propagated video after new account follow', async function () {
601 this.timeout(60000) 627 this.timeout(60000)
602 628
603 await servers[0].follows.follow({ handles: [ 'root_channel@' + servers[2].host ] }) 629 await servers[2].follows.follow({ handles: [ 'root@' + servers[0].host ] })
604 630
605 await waitJobs(servers) 631 await waitJobs(servers)
606 632
607 const video = await servers[0].videos.find({ name: 'server3-7' }) 633 const video = await servers[2].videos.find({ name: 'video to add' })
608
609 expect(video).to.exist 634 expect(video).to.exist
610 }) 635 })
611 })
612 636
613 after(async function () { 637 after(async function () {
614 await cleanupTests(servers) 638 await cleanupTests(servers)
639 })
615 }) 640 })
616}) 641})
diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts
index aad0d231a..a1bf189fa 100644
--- a/server/tests/api/server/stats.ts
+++ b/server/tests/api/server/stats.ts
@@ -194,7 +194,7 @@ describe('Test stats (excluding redundancy)', function () {
194 newConfig: { 194 newConfig: {
195 transcoding: { 195 transcoding: {
196 enabled: true, 196 enabled: true,
197 webtorrent: { 197 webVideos: {
198 enabled: true 198 enabled: true
199 }, 199 },
200 hls: { 200 hls: {
diff --git a/server/tests/api/transcoding/audio-only.ts b/server/tests/api/transcoding/audio-only.ts
index 1e31418e7..f4cc012ef 100644
--- a/server/tests/api/transcoding/audio-only.ts
+++ b/server/tests/api/transcoding/audio-only.ts
@@ -14,7 +14,7 @@ import {
14describe('Test audio only video transcoding', function () { 14describe('Test audio only video transcoding', function () {
15 let servers: PeerTubeServer[] = [] 15 let servers: PeerTubeServer[] = []
16 let videoUUID: string 16 let videoUUID: string
17 let webtorrentAudioFileUrl: string 17 let webVideoAudioFileUrl: string
18 let fragmentedAudioFileUrl: string 18 let fragmentedAudioFileUrl: string
19 19
20 before(async function () { 20 before(async function () {
@@ -37,7 +37,7 @@ describe('Test audio only video transcoding', function () {
37 hls: { 37 hls: {
38 enabled: true 38 enabled: true
39 }, 39 },
40 webtorrent: { 40 web_videos: {
41 enabled: true 41 enabled: true
42 } 42 }
43 } 43 }
@@ -71,7 +71,7 @@ describe('Test audio only video transcoding', function () {
71 } 71 }
72 72
73 if (server.serverNumber === 1) { 73 if (server.serverNumber === 1) {
74 webtorrentAudioFileUrl = video.files[2].fileUrl 74 webVideoAudioFileUrl = video.files[2].fileUrl
75 fragmentedAudioFileUrl = video.streamingPlaylists[0].files[2].fileUrl 75 fragmentedAudioFileUrl = video.streamingPlaylists[0].files[2].fileUrl
76 } 76 }
77 } 77 }
@@ -79,7 +79,7 @@ describe('Test audio only video transcoding', function () {
79 79
80 it('0p transcoded video should not have video', async function () { 80 it('0p transcoded video should not have video', async function () {
81 const paths = [ 81 const paths = [
82 servers[0].servers.buildWebTorrentFilePath(webtorrentAudioFileUrl), 82 servers[0].servers.buildWebVideoFilePath(webVideoAudioFileUrl),
83 servers[0].servers.buildFragmentedFilePath(videoUUID, fragmentedAudioFileUrl) 83 servers[0].servers.buildFragmentedFilePath(videoUUID, fragmentedAudioFileUrl)
84 ] 84 ]
85 85
diff --git a/server/tests/api/transcoding/create-transcoding.ts b/server/tests/api/transcoding/create-transcoding.ts
index d6f5b01dc..9a891043c 100644
--- a/server/tests/api/transcoding/create-transcoding.ts
+++ b/server/tests/api/transcoding/create-transcoding.ts
@@ -96,12 +96,12 @@ function runTests (enableObjectStorage: boolean) {
96 } 96 }
97 }) 97 })
98 98
99 it('Should generate WebTorrent', async function () { 99 it('Should generate Web Video', async function () {
100 this.timeout(60000) 100 this.timeout(60000)
101 101
102 await servers[0].videos.runTranscoding({ 102 await servers[0].videos.runTranscoding({
103 videoId: videoUUID, 103 videoId: videoUUID,
104 transcodingType: 'webtorrent' 104 transcodingType: 'web-video'
105 }) 105 })
106 106
107 await waitJobs(servers) 107 await waitJobs(servers)
@@ -117,13 +117,13 @@ function runTests (enableObjectStorage: boolean) {
117 } 117 }
118 }) 118 })
119 119
120 it('Should generate WebTorrent from HLS only video', async function () { 120 it('Should generate Web Video from HLS only video', async function () {
121 this.timeout(60000) 121 this.timeout(60000)
122 122
123 await servers[0].videos.removeAllWebTorrentFiles({ videoId: videoUUID }) 123 await servers[0].videos.removeAllWebVideoFiles({ videoId: videoUUID })
124 await waitJobs(servers) 124 await waitJobs(servers)
125 125
126 await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'webtorrent' }) 126 await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
127 await waitJobs(servers) 127 await waitJobs(servers)
128 128
129 for (const server of servers) { 129 for (const server of servers) {
@@ -137,13 +137,13 @@ function runTests (enableObjectStorage: boolean) {
137 } 137 }
138 }) 138 })
139 139
140 it('Should only generate WebTorrent', async function () { 140 it('Should only generate Web Video', async function () {
141 this.timeout(60000) 141 this.timeout(60000)
142 142
143 await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID }) 143 await servers[0].videos.removeHLSPlaylist({ videoId: videoUUID })
144 await waitJobs(servers) 144 await waitJobs(servers)
145 145
146 await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'webtorrent' }) 146 await servers[0].videos.runTranscoding({ videoId: videoUUID, transcodingType: 'web-video' })
147 await waitJobs(servers) 147 await waitJobs(servers)
148 148
149 for (const server of servers) { 149 for (const server of servers) {
@@ -165,7 +165,7 @@ function runTests (enableObjectStorage: boolean) {
165 enabled: true, 165 enabled: true,
166 resolutions: ConfigCommand.getCustomConfigResolutions(false), 166 resolutions: ConfigCommand.getCustomConfigResolutions(false),
167 167
168 webtorrent: { 168 webVideos: {
169 enabled: true 169 enabled: true
170 }, 170 },
171 hls: { 171 hls: {
@@ -201,7 +201,7 @@ function runTests (enableObjectStorage: boolean) {
201 enabled: true, 201 enabled: true,
202 resolutions: ConfigCommand.getCustomConfigResolutions(true), 202 resolutions: ConfigCommand.getCustomConfigResolutions(true),
203 203
204 webtorrent: { 204 webVideos: {
205 enabled: true 205 enabled: true
206 }, 206 },
207 hls: { 207 hls: {
diff --git a/server/tests/api/transcoding/hls.ts b/server/tests/api/transcoding/hls.ts
index c668d7e0b..d67043c2a 100644
--- a/server/tests/api/transcoding/hls.ts
+++ b/server/tests/api/transcoding/hls.ts
@@ -75,8 +75,8 @@ describe('Test HLS videos', function () {
75 75
76 it('Should have the playlists/segment deleted from the disk', async function () { 76 it('Should have the playlists/segment deleted from the disk', async function () {
77 for (const server of servers) { 77 for (const server of servers) {
78 await checkDirectoryIsEmpty(server, 'videos', [ 'private' ]) 78 await checkDirectoryIsEmpty(server, 'web-videos', [ 'private' ])
79 await checkDirectoryIsEmpty(server, join('videos', 'private')) 79 await checkDirectoryIsEmpty(server, join('web-videos', 'private'))
80 80
81 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls'), [ 'private' ]) 81 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls'), [ 'private' ])
82 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls', 'private')) 82 await checkDirectoryIsEmpty(server, join('streaming-playlists', 'hls', 'private'))
@@ -111,7 +111,7 @@ describe('Test HLS videos', function () {
111 await doubleFollow(servers[0], servers[1]) 111 await doubleFollow(servers[0], servers[1])
112 }) 112 })
113 113
114 describe('With WebTorrent & HLS enabled', function () { 114 describe('With Web Video & HLS enabled', function () {
115 runTestSuite(false) 115 runTestSuite(false)
116 }) 116 })
117 117
@@ -136,7 +136,7 @@ describe('Test HLS videos', function () {
136 hls: { 136 hls: {
137 enabled: true 137 enabled: true
138 }, 138 },
139 webtorrent: { 139 webVideos: {
140 enabled: false 140 enabled: false
141 } 141 }
142 } 142 }
diff --git a/server/tests/api/transcoding/transcoder.ts b/server/tests/api/transcoding/transcoder.ts
index 8a0a7f6d2..5386d236f 100644
--- a/server/tests/api/transcoding/transcoder.ts
+++ b/server/tests/api/transcoding/transcoder.ts
@@ -31,7 +31,7 @@ function updateConfigForTranscoding (server: PeerTubeServer) {
31 allowAdditionalExtensions: true, 31 allowAdditionalExtensions: true,
32 allowAudioFiles: true, 32 allowAudioFiles: true,
33 hls: { enabled: true }, 33 hls: { enabled: true },
34 webtorrent: { enabled: true }, 34 webVideos: { enabled: true },
35 resolutions: { 35 resolutions: {
36 '0p': false, 36 '0p': false,
37 '144p': true, 37 '144p': true,
@@ -251,7 +251,7 @@ describe('Test video transcoding', function () {
251 expect(videoDetails.files).to.have.lengthOf(5) 251 expect(videoDetails.files).to.have.lengthOf(5)
252 252
253 const file = videoDetails.files.find(f => f.resolution.id === 240) 253 const file = videoDetails.files.find(f => f.resolution.id === 240)
254 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 254 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
255 const probe = await getAudioStream(path) 255 const probe = await getAudioStream(path)
256 256
257 if (probe.audioStream) { 257 if (probe.audioStream) {
@@ -281,7 +281,7 @@ describe('Test video transcoding', function () {
281 const videoDetails = await server.videos.get({ id: video.id }) 281 const videoDetails = await server.videos.get({ id: video.id })
282 282
283 const file = videoDetails.files.find(f => f.resolution.id === 240) 283 const file = videoDetails.files.find(f => f.resolution.id === 240)
284 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 284 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
285 285
286 expect(await hasAudioStream(path)).to.be.false 286 expect(await hasAudioStream(path)).to.be.false
287 } 287 }
@@ -310,7 +310,7 @@ describe('Test video transcoding', function () {
310 const fixtureVideoProbe = await getAudioStream(fixturePath) 310 const fixtureVideoProbe = await getAudioStream(fixturePath)
311 311
312 const file = videoDetails.files.find(f => f.resolution.id === 240) 312 const file = videoDetails.files.find(f => f.resolution.id === 240)
313 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 313 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
314 314
315 const videoProbe = await getAudioStream(path) 315 const videoProbe = await getAudioStream(path)
316 316
@@ -333,7 +333,7 @@ describe('Test video transcoding', function () {
333 newConfig: { 333 newConfig: {
334 transcoding: { 334 transcoding: {
335 hls: { enabled: true }, 335 hls: { enabled: true },
336 webtorrent: { enabled: true }, 336 webVideos: { enabled: true },
337 resolutions: { 337 resolutions: {
338 '0p': false, 338 '0p': false,
339 '144p': false, 339 '144p': false,
@@ -353,7 +353,7 @@ describe('Test video transcoding', function () {
353 it('Should merge an audio file with the preview file', async function () { 353 it('Should merge an audio file with the preview file', async function () {
354 this.timeout(60_000) 354 this.timeout(60_000)
355 355
356 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 356 const attributes = { name: 'audio_with_preview', previewfile: 'custom-preview.jpg', fixture: 'sample.ogg' }
357 await servers[1].videos.upload({ attributes, mode }) 357 await servers[1].videos.upload({ attributes, mode })
358 358
359 await waitJobs(servers) 359 await waitJobs(servers)
@@ -405,7 +405,7 @@ describe('Test video transcoding', function () {
405 newConfig: { 405 newConfig: {
406 transcoding: { 406 transcoding: {
407 hls: { enabled: true }, 407 hls: { enabled: true },
408 webtorrent: { enabled: true }, 408 webVideos: { enabled: true },
409 resolutions: { 409 resolutions: {
410 '0p': true, 410 '0p': true,
411 '144p': false, 411 '144p': false,
@@ -416,7 +416,7 @@ describe('Test video transcoding', function () {
416 } 416 }
417 }) 417 })
418 418
419 const attributes = { name: 'audio_with_preview', previewfile: 'preview.jpg', fixture: 'sample.ogg' } 419 const attributes = { name: 'audio_with_preview', previewfile: 'custom-preview.jpg', fixture: 'sample.ogg' }
420 const { id } = await servers[1].videos.upload({ attributes, mode }) 420 const { id } = await servers[1].videos.upload({ attributes, mode })
421 421
422 await waitJobs(servers) 422 await waitJobs(servers)
@@ -472,14 +472,14 @@ describe('Test video transcoding', function () {
472 472
473 for (const resolution of [ 144, 240, 360, 480 ]) { 473 for (const resolution of [ 144, 240, 360, 480 ]) {
474 const file = videoDetails.files.find(f => f.resolution.id === resolution) 474 const file = videoDetails.files.find(f => f.resolution.id === resolution)
475 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 475 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
476 const fps = await getVideoStreamFPS(path) 476 const fps = await getVideoStreamFPS(path)
477 477
478 expect(fps).to.be.below(31) 478 expect(fps).to.be.below(31)
479 } 479 }
480 480
481 const file = videoDetails.files.find(f => f.resolution.id === 720) 481 const file = videoDetails.files.find(f => f.resolution.id === 720)
482 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 482 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
483 const fps = await getVideoStreamFPS(path) 483 const fps = await getVideoStreamFPS(path)
484 484
485 expect(fps).to.be.above(58).and.below(62) 485 expect(fps).to.be.above(58).and.below(62)
@@ -516,14 +516,14 @@ describe('Test video transcoding', function () {
516 516
517 { 517 {
518 const file = video.files.find(f => f.resolution.id === 240) 518 const file = video.files.find(f => f.resolution.id === 240)
519 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 519 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
520 const fps = await getVideoStreamFPS(path) 520 const fps = await getVideoStreamFPS(path)
521 expect(fps).to.be.equal(25) 521 expect(fps).to.be.equal(25)
522 } 522 }
523 523
524 { 524 {
525 const file = video.files.find(f => f.resolution.id === 720) 525 const file = video.files.find(f => f.resolution.id === 720)
526 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 526 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
527 const fps = await getVideoStreamFPS(path) 527 const fps = await getVideoStreamFPS(path)
528 expect(fps).to.be.equal(59) 528 expect(fps).to.be.equal(59)
529 } 529 }
@@ -556,7 +556,7 @@ describe('Test video transcoding', function () {
556 556
557 for (const resolution of [ 240, 360, 480, 720, 1080 ]) { 557 for (const resolution of [ 240, 360, 480, 720, 1080 ]) {
558 const file = video.files.find(f => f.resolution.id === resolution) 558 const file = video.files.find(f => f.resolution.id === resolution)
559 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 559 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
560 560
561 const bitrate = await getVideoStreamBitrate(path) 561 const bitrate = await getVideoStreamBitrate(path)
562 const fps = await getVideoStreamFPS(path) 562 const fps = await getVideoStreamFPS(path)
@@ -586,7 +586,7 @@ describe('Test video transcoding', function () {
586 '1440p': true, 586 '1440p': true,
587 '2160p': true 587 '2160p': true
588 }, 588 },
589 webtorrent: { enabled: true }, 589 webVideos: { enabled: true },
590 hls: { enabled: true } 590 hls: { enabled: true }
591 } 591 }
592 } 592 }
@@ -607,7 +607,7 @@ describe('Test video transcoding', function () {
607 for (const r of resolutions) { 607 for (const r of resolutions) {
608 const file = video.files.find(f => f.resolution.id === r) 608 const file = video.files.find(f => f.resolution.id === r)
609 609
610 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 610 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
611 const bitrate = await getVideoStreamBitrate(path) 611 const bitrate = await getVideoStreamBitrate(path)
612 612
613 const inputBitrate = 60_000 613 const inputBitrate = 60_000
@@ -631,7 +631,7 @@ describe('Test video transcoding', function () {
631 { 631 {
632 const video = await servers[1].videos.get({ id: videoUUID }) 632 const video = await servers[1].videos.get({ id: videoUUID })
633 const file = video.files.find(f => f.resolution.id === 240) 633 const file = video.files.find(f => f.resolution.id === 240)
634 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 634 const path = servers[1].servers.buildWebVideoFilePath(file.fileUrl)
635 635
636 const probe = await ffprobePromise(path) 636 const probe = await ffprobePromise(path)
637 const metadata = new VideoFileMetadata(probe) 637 const metadata = new VideoFileMetadata(probe)
@@ -704,14 +704,14 @@ describe('Test video transcoding', function () {
704 expect(transcodingJobs).to.have.lengthOf(16) 704 expect(transcodingJobs).to.have.lengthOf(16)
705 705
706 const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls') 706 const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls')
707 const webtorrentJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-webtorrent') 707 const webVideoJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-web-video')
708 const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-webtorrent') 708 const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-web-video')
709 709
710 expect(hlsJobs).to.have.lengthOf(8) 710 expect(hlsJobs).to.have.lengthOf(8)
711 expect(webtorrentJobs).to.have.lengthOf(7) 711 expect(webVideoJobs).to.have.lengthOf(7)
712 expect(optimizeJobs).to.have.lengthOf(1) 712 expect(optimizeJobs).to.have.lengthOf(1)
713 713
714 for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) { 714 for (const j of optimizeJobs.concat(hlsJobs.concat(webVideoJobs))) {
715 expect(j.priority).to.be.greaterThan(100) 715 expect(j.priority).to.be.greaterThan(100)
716 expect(j.priority).to.be.lessThan(150) 716 expect(j.priority).to.be.lessThan(150)
717 } 717 }
@@ -728,7 +728,7 @@ describe('Test video transcoding', function () {
728 transcoding: { 728 transcoding: {
729 enabled: true, 729 enabled: true,
730 hls: { enabled: true }, 730 hls: { enabled: true },
731 webtorrent: { enabled: true }, 731 webVideos: { enabled: true },
732 resolutions: { 732 resolutions: {
733 '0p': false, 733 '0p': false,
734 '144p': false, 734 '144p': false,
diff --git a/server/tests/api/transcoding/update-while-transcoding.ts b/server/tests/api/transcoding/update-while-transcoding.ts
index 61655f102..cfb4fa0cc 100644
--- a/server/tests/api/transcoding/update-while-transcoding.ts
+++ b/server/tests/api/transcoding/update-while-transcoding.ts
@@ -96,7 +96,7 @@ describe('Test update video privacy while transcoding', function () {
96 await doubleFollow(servers[0], servers[1]) 96 await doubleFollow(servers[0], servers[1])
97 }) 97 })
98 98
99 describe('With WebTorrent & HLS enabled', function () { 99 describe('With Web Video & HLS enabled', function () {
100 runTestSuite(false) 100 runTestSuite(false)
101 }) 101 })
102 102
@@ -121,7 +121,7 @@ describe('Test update video privacy while transcoding', function () {
121 hls: { 121 hls: {
122 enabled: true 122 enabled: true
123 }, 123 },
124 webtorrent: { 124 webVideos: {
125 enabled: false 125 enabled: false
126 } 126 }
127 } 127 }
diff --git a/server/tests/api/transcoding/video-studio.ts b/server/tests/api/transcoding/video-studio.ts
index d1298caf7..ba68f8e24 100644
--- a/server/tests/api/transcoding/video-studio.ts
+++ b/server/tests/api/transcoding/video-studio.ts
@@ -241,7 +241,7 @@ describe('Test video studio', function () {
241 { 241 {
242 name: 'add-watermark', 242 name: 'add-watermark',
243 options: { 243 options: {
244 file: 'thumbnail.png' 244 file: 'custom-thumbnail.png'
245 } 245 }
246 } 246 }
247 ]) 247 ])
@@ -273,11 +273,11 @@ describe('Test video studio', function () {
273 describe('HLS only studio edition', function () { 273 describe('HLS only studio edition', function () {
274 274
275 before(async function () { 275 before(async function () {
276 // Disable webtorrent 276 // Disable Web Videos
277 await servers[0].config.updateExistingSubConfig({ 277 await servers[0].config.updateExistingSubConfig({
278 newConfig: { 278 newConfig: {
279 transcoding: { 279 transcoding: {
280 webtorrent: { 280 webVideos: {
281 enabled: false 281 enabled: false
282 } 282 }
283 } 283 }
@@ -354,8 +354,8 @@ describe('Test video studio', function () {
354 expect(oldFileUrls).to.not.include(f.fileUrl) 354 expect(oldFileUrls).to.not.include(f.fileUrl)
355 } 355 }
356 356
357 for (const webtorrentFile of video.files) { 357 for (const webVideoFile of video.files) {
358 expectStartWith(webtorrentFile.fileUrl, objectStorage.getMockWebVideosBaseUrl()) 358 expectStartWith(webVideoFile.fileUrl, objectStorage.getMockWebVideosBaseUrl())
359 } 359 }
360 360
361 for (const hlsFile of video.streamingPlaylists[0].files) { 361 for (const hlsFile of video.streamingPlaylists[0].files) {
diff --git a/server/tests/api/users/user-videos.ts b/server/tests/api/users/user-videos.ts
index 696949504..77226e48e 100644
--- a/server/tests/api/users/user-videos.ts
+++ b/server/tests/api/users/user-videos.ts
@@ -184,12 +184,12 @@ describe('Test user videos', function () {
184 } 184 }
185 }) 185 })
186 186
187 it('Should disable webtorrent, enable HLS, and update my quota', async function () { 187 it('Should disable web videos, enable HLS, and update my quota', async function () {
188 this.timeout(160000) 188 this.timeout(160000)
189 189
190 { 190 {
191 const config = await server.config.getCustomConfig() 191 const config = await server.config.getCustomConfig()
192 config.transcoding.webtorrent.enabled = false 192 config.transcoding.webVideos.enabled = false
193 config.transcoding.hls.enabled = true 193 config.transcoding.hls.enabled = true
194 config.transcoding.enabled = true 194 config.transcoding.enabled = true
195 await server.config.updateCustomSubConfig({ newConfig: config }) 195 await server.config.updateCustomSubConfig({ newConfig: config })
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index 1c00f9a93..67ade1d0d 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -229,25 +229,13 @@ describe('Test users', function () {
229 }) 229 })
230 230
231 it('Should be able to change the p2p attribute', async function () { 231 it('Should be able to change the p2p attribute', async function () {
232 { 232 await server.users.updateMe({
233 await server.users.updateMe({ 233 token: userToken,
234 token: userToken, 234 p2pEnabled: true
235 webTorrentEnabled: false 235 })
236 })
237
238 const user = await server.users.getMyInfo({ token: userToken })
239 expect(user.p2pEnabled).to.be.false
240 }
241
242 {
243 await server.users.updateMe({
244 token: userToken,
245 p2pEnabled: true
246 })
247 236
248 const user = await server.users.getMyInfo({ token: userToken }) 237 const user = await server.users.getMyInfo({ token: userToken })
249 expect(user.p2pEnabled).to.be.true 238 expect(user.p2pEnabled).to.be.true
250 }
251 }) 239 })
252 240
253 it('Should be able to change the email attribute', async function () { 241 it('Should be able to change the email attribute', async function () {
diff --git a/server/tests/api/videos/index.ts b/server/tests/api/videos/index.ts
index 357c08199..9c79b3aa6 100644
--- a/server/tests/api/videos/index.ts
+++ b/server/tests/api/videos/index.ts
@@ -20,3 +20,4 @@ import './videos-history'
20import './videos-overview' 20import './videos-overview'
21import './video-source' 21import './video-source'
22import './video-static-file-privacy' 22import './video-static-file-privacy'
23import './video-storyboard'
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index 27ba00d3d..e9aa0e3a1 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -9,7 +9,7 @@ import {
9 completeVideoCheck, 9 completeVideoCheck,
10 dateIsValid, 10 dateIsValid,
11 saveVideoInServers, 11 saveVideoInServers,
12 testImage 12 testImageGeneratedByFFmpeg
13} from '@server/tests/shared' 13} from '@server/tests/shared'
14import { buildAbsoluteFixturePath, wait } from '@shared/core-utils' 14import { buildAbsoluteFixturePath, wait } from '@shared/core-utils'
15import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models' 15import { HttpStatusCode, VideoCommentThreadTree, VideoPrivacy } from '@shared/models'
@@ -70,8 +70,9 @@ describe('Test multiple servers', function () {
70 }) 70 })
71 71
72 describe('Should upload the video and propagate on each server', function () { 72 describe('Should upload the video and propagate on each server', function () {
73
73 it('Should upload the video on server 1 and propagate on each server', async function () { 74 it('Should upload the video on server 1 and propagate on each server', async function () {
74 this.timeout(25000) 75 this.timeout(60000)
75 76
76 const attributes = { 77 const attributes = {
77 name: 'my super name for server 1', 78 name: 'my super name for server 1',
@@ -175,8 +176,8 @@ describe('Test multiple servers', function () {
175 support: 'my super support text for server 2', 176 support: 'my super support text for server 2',
176 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], 177 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
177 fixture: 'video_short2.webm', 178 fixture: 'video_short2.webm',
178 thumbnailfile: 'thumbnail.jpg', 179 thumbnailfile: 'custom-thumbnail.jpg',
179 previewfile: 'preview.jpg' 180 previewfile: 'custom-preview.jpg'
180 } 181 }
181 await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' }) 182 await servers[1].videos.upload({ token: userAccessToken, attributes, mode: 'resumable' })
182 183
@@ -229,8 +230,8 @@ describe('Test multiple servers', function () {
229 size: 750000 230 size: 750000
230 } 231 }
231 ], 232 ],
232 thumbnailfile: 'thumbnail', 233 thumbnailfile: 'custom-thumbnail',
233 previewfile: 'preview' 234 previewfile: 'custom-preview'
234 } 235 }
235 236
236 const { data } = await server.videos.list() 237 const { data } = await server.videos.list()
@@ -619,9 +620,9 @@ describe('Test multiple servers', function () {
619 description: 'my super description updated', 620 description: 'my super description updated',
620 support: 'my super support text updated', 621 support: 'my super support text updated',
621 tags: [ 'tag_up_1', 'tag_up_2' ], 622 tags: [ 'tag_up_1', 'tag_up_2' ],
622 thumbnailfile: 'thumbnail.jpg', 623 thumbnailfile: 'custom-thumbnail.jpg',
623 originallyPublishedAt: '2019-02-11T13:38:14.449Z', 624 originallyPublishedAt: '2019-02-11T13:38:14.449Z',
624 previewfile: 'preview.jpg' 625 previewfile: 'custom-preview.jpg'
625 } 626 }
626 627
627 updatedAtMin = new Date() 628 updatedAtMin = new Date()
@@ -674,8 +675,8 @@ describe('Test multiple servers', function () {
674 size: 292677 675 size: 292677
675 } 676 }
676 ], 677 ],
677 thumbnailfile: 'thumbnail', 678 thumbnailfile: 'custom-thumbnail',
678 previewfile: 'preview' 679 previewfile: 'custom-preview'
679 } 680 }
680 await completeVideoCheck({ server, originServer: servers[2], videoUUID: videoUpdated.uuid, attributes: checkAttributes }) 681 await completeVideoCheck({ server, originServer: servers[2], videoUUID: videoUpdated.uuid, attributes: checkAttributes })
681 } 682 }
@@ -685,7 +686,7 @@ describe('Test multiple servers', function () {
685 this.timeout(30000) 686 this.timeout(30000)
686 687
687 const attributes = { 688 const attributes = {
688 thumbnailfile: 'thumbnail.jpg' 689 thumbnailfile: 'custom-thumbnail.jpg'
689 } 690 }
690 691
691 updatedAtMin = new Date() 692 updatedAtMin = new Date()
@@ -761,7 +762,7 @@ describe('Test multiple servers', function () {
761 for (const server of servers) { 762 for (const server of servers) {
762 const video = await server.videos.get({ id: videoUUID }) 763 const video = await server.videos.get({ id: videoUUID })
763 764
764 await testImage(server.url, 'video_short1-preview.webm', video.previewPath) 765 await testImageGeneratedByFFmpeg(server.url, 'video_short1-preview.webm', video.previewPath)
765 } 766 }
766 }) 767 })
767 }) 768 })
diff --git a/server/tests/api/videos/resumable-upload.ts b/server/tests/api/videos/resumable-upload.ts
index 2fbefb392..91eb61833 100644
--- a/server/tests/api/videos/resumable-upload.ts
+++ b/server/tests/api/videos/resumable-upload.ts
@@ -93,10 +93,10 @@ describe('Test resumable upload', function () {
93 expect((await stat(filePath)).size).to.equal(expectedSize) 93 expect((await stat(filePath)).size).to.equal(expectedSize)
94 } 94 }
95 95
96 async function countResumableUploads () { 96 async function countResumableUploads (wait?: number) {
97 const subPath = join('tmp', 'resumable-uploads') 97 const subPath = join('tmp', 'resumable-uploads')
98 const filePath = server.servers.buildDirectory(subPath) 98 const filePath = server.servers.buildDirectory(subPath)
99 99 await new Promise(resolve => setTimeout(resolve, wait))
100 const files = await readdir(filePath) 100 const files = await readdir(filePath)
101 return files.length 101 return files.length
102 } 102 }
@@ -122,14 +122,20 @@ describe('Test resumable upload', function () {
122 122
123 describe('Directory cleaning', function () { 123 describe('Directory cleaning', function () {
124 124
125 // FIXME: https://github.com/kukhariev/node-uploadx/pull/524/files#r852989382 125 it('Should correctly delete files after an upload', async function () {
126 // it('Should correctly delete files after an upload', async function () { 126 const uploadId = await prepareUpload()
127 // const uploadId = await prepareUpload() 127 await sendChunks({ pathUploadId: uploadId })
128 // await sendChunks({ pathUploadId: uploadId }) 128 await server.videos.endResumableUpload({ pathUploadId: uploadId })
129 // await server.videos.endResumableUpload({ pathUploadId: uploadId }) 129
130 expect(await countResumableUploads()).to.equal(0)
131 })
132
133 it('Should correctly delete corrupt files', async function () {
134 const uploadId = await prepareUpload({ size: 8 * 1024 })
135 await sendChunks({ pathUploadId: uploadId, size: 8 * 1024, expectedStatus: HttpStatusCode.UNPROCESSABLE_ENTITY_422 })
130 136
131 // expect(await countResumableUploads()).to.equal(0) 137 expect(await countResumableUploads(2000)).to.equal(0)
132 // }) 138 })
133 139
134 it('Should not delete files after an unfinished upload', async function () { 140 it('Should not delete files after an unfinished upload', async function () {
135 await prepareUpload() 141 await prepareUpload()
@@ -254,6 +260,24 @@ describe('Test resumable upload', function () {
254 expect(result2.headers['x-resumable-upload-cached']).to.not.exist 260 expect(result2.headers['x-resumable-upload-cached']).to.not.exist
255 }) 261 })
256 262
263 it('Should not cache after video deletion', async function () {
264 const originalName = 'toto.mp4'
265 const lastModified = new Date().getTime()
266
267 const uploadId1 = await prepareUpload({ originalName, lastModified })
268 const result1 = await sendChunks({ pathUploadId: uploadId1 })
269 await server.videos.remove({ id: result1.body.video.uuid })
270
271 const uploadId2 = await prepareUpload({ originalName, lastModified })
272 const result2 = await sendChunks({ pathUploadId: uploadId2 })
273 expect(result1.body.video.uuid).to.not.equal(result2.body.video.uuid)
274
275 expect(result2.headers['x-resumable-upload-cached']).to.not.exist
276
277 await checkFileSize(uploadId1, null)
278 await checkFileSize(uploadId2, null)
279 })
280
257 it('Should refuse an invalid digest', async function () { 281 it('Should refuse an invalid digest', async function () {
258 const uploadId = await prepareUpload({ token: server.accessToken }) 282 const uploadId = await prepareUpload({ token: server.accessToken })
259 283
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index 0cb64d5a5..66414aa5b 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -1,7 +1,7 @@
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 { expect } from 'chai'
4import { checkVideoFilesWereRemoved, completeVideoCheck, testImage } from '@server/tests/shared' 4import { checkVideoFilesWereRemoved, completeVideoCheck, testImageGeneratedByFFmpeg } from '@server/tests/shared'
5import { wait } from '@shared/core-utils' 5import { wait } from '@shared/core-utils'
6import { Video, VideoPrivacy } from '@shared/models' 6import { Video, VideoPrivacy } from '@shared/models'
7import { 7import {
@@ -260,7 +260,7 @@ describe('Test a single server', function () {
260 260
261 for (const video of data) { 261 for (const video of data) {
262 const videoName = video.name.replace(' name', '') 262 const videoName = video.name.replace(' name', '')
263 await testImage(server.url, videoName, video.thumbnailPath) 263 await testImageGeneratedByFFmpeg(server.url, videoName, video.thumbnailPath)
264 } 264 }
265 }) 265 })
266 266
diff --git a/server/tests/api/videos/video-files.ts b/server/tests/api/videos/video-files.ts
index 8c913bf31..0a183c44d 100644
--- a/server/tests/api/videos/video-files.ts
+++ b/server/tests/api/videos/video-files.ts
@@ -48,10 +48,10 @@ describe('Test videos files', function () {
48 await waitJobs(servers) 48 await waitJobs(servers)
49 }) 49 })
50 50
51 it('Should delete webtorrent files', async function () { 51 it('Should delete web video files', async function () {
52 this.timeout(30_000) 52 this.timeout(30_000)
53 53
54 await servers[0].videos.removeAllWebTorrentFiles({ videoId: validId1 }) 54 await servers[0].videos.removeAllWebVideoFiles({ videoId: validId1 })
55 55
56 await waitJobs(servers) 56 await waitJobs(servers)
57 57
@@ -80,15 +80,15 @@ describe('Test videos files', function () {
80 }) 80 })
81 81
82 describe('When deleting a specific file', function () { 82 describe('When deleting a specific file', function () {
83 let webtorrentId: string 83 let webVideoId: string
84 let hlsId: string 84 let hlsId: string
85 85
86 before(async function () { 86 before(async function () {
87 this.timeout(120_000) 87 this.timeout(120_000)
88 88
89 { 89 {
90 const { uuid } = await servers[0].videos.quickUpload({ name: 'webtorrent' }) 90 const { uuid } = await servers[0].videos.quickUpload({ name: 'web-video' })
91 webtorrentId = uuid 91 webVideoId = uuid
92 } 92 }
93 93
94 { 94 {
@@ -99,38 +99,38 @@ describe('Test videos files', function () {
99 await waitJobs(servers) 99 await waitJobs(servers)
100 }) 100 })
101 101
102 it('Shoulde delete a webtorrent file', async function () { 102 it('Shoulde delete a web video file', async function () {
103 this.timeout(30_000) 103 this.timeout(30_000)
104 104
105 const video = await servers[0].videos.get({ id: webtorrentId }) 105 const video = await servers[0].videos.get({ id: webVideoId })
106 const files = video.files 106 const files = video.files
107 107
108 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: files[0].id }) 108 await servers[0].videos.removeWebVideoFile({ videoId: webVideoId, fileId: files[0].id })
109 109
110 await waitJobs(servers) 110 await waitJobs(servers)
111 111
112 for (const server of servers) { 112 for (const server of servers) {
113 const video = await server.videos.get({ id: webtorrentId }) 113 const video = await server.videos.get({ id: webVideoId })
114 114
115 expect(video.files).to.have.lengthOf(files.length - 1) 115 expect(video.files).to.have.lengthOf(files.length - 1)
116 expect(video.files.find(f => f.id === files[0].id)).to.not.exist 116 expect(video.files.find(f => f.id === files[0].id)).to.not.exist
117 } 117 }
118 }) 118 })
119 119
120 it('Should delete all webtorrent files', async function () { 120 it('Should delete all web video files', async function () {
121 this.timeout(30_000) 121 this.timeout(30_000)
122 122
123 const video = await servers[0].videos.get({ id: webtorrentId }) 123 const video = await servers[0].videos.get({ id: webVideoId })
124 const files = video.files 124 const files = video.files
125 125
126 for (const file of files) { 126 for (const file of files) {
127 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentId, fileId: file.id }) 127 await servers[0].videos.removeWebVideoFile({ videoId: webVideoId, fileId: file.id })
128 } 128 }
129 129
130 await waitJobs(servers) 130 await waitJobs(servers)
131 131
132 for (const server of servers) { 132 for (const server of servers) {
133 const video = await server.videos.get({ id: webtorrentId }) 133 const video = await server.videos.get({ id: webVideoId })
134 134
135 expect(video.files).to.have.lengthOf(0) 135 expect(video.files).to.have.lengthOf(0)
136 } 136 }
@@ -182,16 +182,16 @@ describe('Test videos files', function () {
182 it('Should not delete last file of a video', async function () { 182 it('Should not delete last file of a video', async function () {
183 this.timeout(60_000) 183 this.timeout(60_000)
184 184
185 const webtorrentOnly = await servers[0].videos.get({ id: hlsId }) 185 const webVideoOnly = await servers[0].videos.get({ id: hlsId })
186 const hlsOnly = await servers[0].videos.get({ id: webtorrentId }) 186 const hlsOnly = await servers[0].videos.get({ id: webVideoId })
187 187
188 for (let i = 0; i < 4; i++) { 188 for (let i = 0; i < 4; i++) {
189 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentOnly.id, fileId: webtorrentOnly.files[i].id }) 189 await servers[0].videos.removeWebVideoFile({ videoId: webVideoOnly.id, fileId: webVideoOnly.files[i].id })
190 await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[i].id }) 190 await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[i].id })
191 } 191 }
192 192
193 const expectedStatus = HttpStatusCode.BAD_REQUEST_400 193 const expectedStatus = HttpStatusCode.BAD_REQUEST_400
194 await servers[0].videos.removeWebTorrentFile({ videoId: webtorrentOnly.id, fileId: webtorrentOnly.files[4].id, expectedStatus }) 194 await servers[0].videos.removeWebVideoFile({ videoId: webVideoOnly.id, fileId: webVideoOnly.files[4].id, expectedStatus })
195 await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[4].id, expectedStatus }) 195 await servers[0].videos.removeHLSFile({ videoId: hlsOnly.id, fileId: hlsOnly.streamingPlaylists[0].files[4].id, expectedStatus })
196 }) 196 })
197 }) 197 })
diff --git a/server/tests/api/videos/video-imports.ts b/server/tests/api/videos/video-imports.ts
index 192b2aeb9..b78b4f344 100644
--- a/server/tests/api/videos/video-imports.ts
+++ b/server/tests/api/videos/video-imports.ts
@@ -3,7 +3,7 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { pathExists, readdir, remove } from 'fs-extra' 4import { pathExists, readdir, remove } from 'fs-extra'
5import { join } from 'path' 5import { join } from 'path'
6import { FIXTURE_URLS, testCaptionFile, testImage } from '@server/tests/shared' 6import { FIXTURE_URLS, testCaptionFile, testImageGeneratedByFFmpeg } from '@server/tests/shared'
7import { areHttpImportTestsDisabled } from '@shared/core-utils' 7import { areHttpImportTestsDisabled } from '@shared/core-utils'
8import { CustomConfig, HttpStatusCode, Video, VideoImportState, VideoPrivacy, VideoResolution, VideoState } from '@shared/models' 8import { CustomConfig, HttpStatusCode, Video, VideoImportState, VideoPrivacy, VideoResolution, VideoState } from '@shared/models'
9import { 9import {
@@ -67,7 +67,7 @@ async function checkVideoServer2 (server: PeerTubeServer, id: number | string) {
67 expect(video.description).to.equal('my super description') 67 expect(video.description).to.equal('my super description')
68 expect(video.tags).to.deep.equal([ 'supertag1', 'supertag2' ]) 68 expect(video.tags).to.deep.equal([ 'supertag1', 'supertag2' ])
69 69
70 await testImage(server.url, 'thumbnail', video.thumbnailPath) 70 await testImageGeneratedByFFmpeg(server.url, 'custom-thumbnail', video.thumbnailPath)
71 71
72 expect(video.files).to.have.lengthOf(1) 72 expect(video.files).to.have.lengthOf(1)
73 73
@@ -119,15 +119,15 @@ describe('Test video imports', function () {
119 expect(video.name).to.equal('small video - youtube') 119 expect(video.name).to.equal('small video - youtube')
120 120
121 { 121 {
122 expect(video.thumbnailPath).to.match(new RegExp(`^/static/thumbnails/.+.jpg$`)) 122 expect(video.thumbnailPath).to.match(new RegExp(`^/lazy-static/thumbnails/.+.jpg$`))
123 expect(video.previewPath).to.match(new RegExp(`^/lazy-static/previews/.+.jpg$`)) 123 expect(video.previewPath).to.match(new RegExp(`^/lazy-static/previews/.+.jpg$`))
124 124
125 const suffix = mode === 'yt-dlp' 125 const suffix = mode === 'yt-dlp'
126 ? '_yt_dlp' 126 ? '_yt_dlp'
127 : '' 127 : ''
128 128
129 await testImage(servers[0].url, 'video_import_thumbnail' + suffix, video.thumbnailPath) 129 await testImageGeneratedByFFmpeg(servers[0].url, 'video_import_thumbnail' + suffix, video.thumbnailPath)
130 await testImage(servers[0].url, 'video_import_preview' + suffix, video.previewPath) 130 await testImageGeneratedByFFmpeg(servers[0].url, 'video_import_preview' + suffix, video.previewPath)
131 } 131 }
132 132
133 const bodyCaptions = await servers[0].captions.list({ videoId: video.id }) 133 const bodyCaptions = await servers[0].captions.list({ videoId: video.id })
@@ -266,7 +266,7 @@ describe('Test video imports', function () {
266 name: 'my super name', 266 name: 'my super name',
267 description: 'my super description', 267 description: 'my super description',
268 tags: [ 'supertag1', 'supertag2' ], 268 tags: [ 'supertag1', 'supertag2' ],
269 thumbnailfile: 'thumbnail.jpg' 269 thumbnailfile: 'custom-thumbnail.jpg'
270 } 270 }
271 }) 271 })
272 expect(video.name).to.equal('my super name') 272 expect(video.name).to.equal('my super name')
@@ -328,7 +328,7 @@ describe('Test video imports', function () {
328 '1440p': false, 328 '1440p': false,
329 '2160p': false 329 '2160p': false
330 }, 330 },
331 webtorrent: { enabled: true }, 331 webVideos: { enabled: true },
332 hls: { enabled: false } 332 hls: { enabled: false }
333 } 333 }
334 } 334 }
diff --git a/server/tests/api/videos/video-passwords.ts b/server/tests/api/videos/video-passwords.ts
new file mode 100644
index 000000000..e01a93a4d
--- /dev/null
+++ b/server/tests/api/videos/video-passwords.ts
@@ -0,0 +1,97 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import {
5 cleanupTests,
6 createSingleServer,
7 VideoPasswordsCommand,
8 PeerTubeServer,
9 setAccessTokensToServers,
10 setDefaultAccountAvatar,
11 setDefaultChannelAvatar
12} from '@shared/server-commands'
13import { VideoPrivacy } from '@shared/models'
14
15describe('Test video passwords', function () {
16 let server: PeerTubeServer
17 let videoUUID: string
18
19 let userAccessTokenServer1: string
20
21 let videoPasswords: string[] = []
22 let command: VideoPasswordsCommand
23
24 before(async function () {
25 this.timeout(30000)
26
27 server = await createSingleServer(1)
28
29 await setAccessTokensToServers([ server ])
30
31 for (let i = 0; i < 10; i++) {
32 videoPasswords.push(`password ${i + 1}`)
33 }
34 const { uuid } = await server.videos.upload({ attributes: { privacy: VideoPrivacy.PASSWORD_PROTECTED, videoPasswords } })
35 videoUUID = uuid
36
37 await setDefaultChannelAvatar(server)
38 await setDefaultAccountAvatar(server)
39
40 userAccessTokenServer1 = await server.users.generateUserAndToken('user1')
41 await setDefaultChannelAvatar(server, 'user1_channel')
42 await setDefaultAccountAvatar(server, userAccessTokenServer1)
43
44 command = server.videoPasswords
45 })
46
47 it('Should list video passwords', async function () {
48 const body = await command.list({ videoId: videoUUID })
49
50 expect(body.total).to.equal(10)
51 expect(body.data).to.be.an('array')
52 expect(body.data).to.have.lengthOf(10)
53 })
54
55 it('Should filter passwords on this video', async function () {
56 const body = await command.list({ videoId: videoUUID, count: 2, start: 3, sort: 'createdAt' })
57
58 expect(body.total).to.equal(10)
59 expect(body.data).to.be.an('array')
60 expect(body.data).to.have.lengthOf(2)
61 expect(body.data[0].password).to.equal('password 4')
62 expect(body.data[1].password).to.equal('password 5')
63 })
64
65 it('Should update password for this video', async function () {
66 videoPasswords = [ 'my super new password 1', 'my super new password 2' ]
67
68 await command.updateAll({ videoId: videoUUID, passwords: videoPasswords })
69 const body = await command.list({ videoId: videoUUID })
70 expect(body.total).to.equal(2)
71 expect(body.data).to.be.an('array')
72 expect(body.data).to.have.lengthOf(2)
73 expect(body.data[0].password).to.equal('my super new password 2')
74 expect(body.data[1].password).to.equal('my super new password 1')
75 })
76
77 it('Should delete one password', async function () {
78 {
79 const body = await command.list({ videoId: videoUUID })
80 expect(body.total).to.equal(2)
81 expect(body.data).to.be.an('array')
82 expect(body.data).to.have.lengthOf(2)
83 await command.remove({ id: body.data[0].id, videoId: videoUUID })
84 }
85 {
86 const body = await command.list({ videoId: videoUUID })
87
88 expect(body.total).to.equal(1)
89 expect(body.data).to.be.an('array')
90 expect(body.data).to.have.lengthOf(1)
91 }
92 })
93
94 after(async function () {
95 await cleanupTests([ server ])
96 })
97})
diff --git a/server/tests/api/videos/video-playlist-thumbnails.ts b/server/tests/api/videos/video-playlist-thumbnails.ts
index 356939b93..c274c20bf 100644
--- a/server/tests/api/videos/video-playlist-thumbnails.ts
+++ b/server/tests/api/videos/video-playlist-thumbnails.ts
@@ -1,7 +1,7 @@
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 { expect } from 'chai'
4import { testImage } from '@server/tests/shared' 4import { testImageGeneratedByFFmpeg } from '@server/tests/shared'
5import { VideoPlaylistPrivacy } from '@shared/models' 5import { VideoPlaylistPrivacy } from '@shared/models'
6import { 6import {
7 cleanupTests, 7 cleanupTests,
@@ -83,7 +83,7 @@ describe('Playlist thumbnail', function () {
83 83
84 for (const server of servers) { 84 for (const server of servers) {
85 const p = await getPlaylistWithoutThumbnail(server) 85 const p = await getPlaylistWithoutThumbnail(server)
86 await testImage(server.url, 'thumbnail-playlist', p.thumbnailPath) 86 await testImageGeneratedByFFmpeg(server.url, 'thumbnail-playlist', p.thumbnailPath)
87 } 87 }
88 }) 88 })
89 89
@@ -95,7 +95,7 @@ describe('Playlist thumbnail', function () {
95 displayName: 'playlist with thumbnail', 95 displayName: 'playlist with thumbnail',
96 privacy: VideoPlaylistPrivacy.PUBLIC, 96 privacy: VideoPlaylistPrivacy.PUBLIC,
97 videoChannelId: servers[1].store.channel.id, 97 videoChannelId: servers[1].store.channel.id,
98 thumbnailfile: 'thumbnail.jpg' 98 thumbnailfile: 'custom-thumbnail.jpg'
99 } 99 }
100 }) 100 })
101 playlistWithThumbnailId = created.id 101 playlistWithThumbnailId = created.id
@@ -110,7 +110,7 @@ describe('Playlist thumbnail', function () {
110 110
111 for (const server of servers) { 111 for (const server of servers) {
112 const p = await getPlaylistWithThumbnail(server) 112 const p = await getPlaylistWithThumbnail(server)
113 await testImage(server.url, 'thumbnail', p.thumbnailPath) 113 await testImageGeneratedByFFmpeg(server.url, 'custom-thumbnail', p.thumbnailPath)
114 } 114 }
115 }) 115 })
116 116
@@ -135,7 +135,7 @@ describe('Playlist thumbnail', function () {
135 135
136 for (const server of servers) { 136 for (const server of servers) {
137 const p = await getPlaylistWithoutThumbnail(server) 137 const p = await getPlaylistWithoutThumbnail(server)
138 await testImage(server.url, 'thumbnail-playlist', p.thumbnailPath) 138 await testImageGeneratedByFFmpeg(server.url, 'thumbnail-playlist', p.thumbnailPath)
139 } 139 }
140 }) 140 })
141 141
@@ -160,7 +160,7 @@ describe('Playlist thumbnail', function () {
160 160
161 for (const server of servers) { 161 for (const server of servers) {
162 const p = await getPlaylistWithThumbnail(server) 162 const p = await getPlaylistWithThumbnail(server)
163 await testImage(server.url, 'thumbnail', p.thumbnailPath) 163 await testImageGeneratedByFFmpeg(server.url, 'custom-thumbnail', p.thumbnailPath)
164 } 164 }
165 }) 165 })
166 166
@@ -176,7 +176,7 @@ describe('Playlist thumbnail', function () {
176 176
177 for (const server of servers) { 177 for (const server of servers) {
178 const p = await getPlaylistWithoutThumbnail(server) 178 const p = await getPlaylistWithoutThumbnail(server)
179 await testImage(server.url, 'thumbnail-playlist', p.thumbnailPath) 179 await testImageGeneratedByFFmpeg(server.url, 'thumbnail-playlist', p.thumbnailPath)
180 } 180 }
181 }) 181 })
182 182
@@ -192,7 +192,7 @@ describe('Playlist thumbnail', function () {
192 192
193 for (const server of servers) { 193 for (const server of servers) {
194 const p = await getPlaylistWithThumbnail(server) 194 const p = await getPlaylistWithThumbnail(server)
195 await testImage(server.url, 'thumbnail', p.thumbnailPath) 195 await testImageGeneratedByFFmpeg(server.url, 'custom-thumbnail', p.thumbnailPath)
196 } 196 }
197 }) 197 })
198 198
@@ -224,7 +224,7 @@ describe('Playlist thumbnail', function () {
224 224
225 for (const server of servers) { 225 for (const server of servers) {
226 const p = await getPlaylistWithThumbnail(server) 226 const p = await getPlaylistWithThumbnail(server)
227 await testImage(server.url, 'thumbnail', p.thumbnailPath) 227 await testImageGeneratedByFFmpeg(server.url, 'custom-thumbnail', p.thumbnailPath)
228 } 228 }
229 }) 229 })
230 230
diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts
index d9c5bdf16..3bfa874cb 100644
--- a/server/tests/api/videos/video-playlists.ts
+++ b/server/tests/api/videos/video-playlists.ts
@@ -1,7 +1,7 @@
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 { expect } from 'chai'
4import { checkPlaylistFilesWereRemoved, testImage } from '@server/tests/shared' 4import { checkPlaylistFilesWereRemoved, testImageGeneratedByFFmpeg } from '@server/tests/shared'
5import { wait } from '@shared/core-utils' 5import { wait } from '@shared/core-utils'
6import { uuidToShort } from '@shared/extra-utils' 6import { uuidToShort } from '@shared/extra-utils'
7import { 7import {
@@ -133,7 +133,7 @@ describe('Test video playlists', function () {
133 displayName: 'my super playlist', 133 displayName: 'my super playlist',
134 privacy: VideoPlaylistPrivacy.PUBLIC, 134 privacy: VideoPlaylistPrivacy.PUBLIC,
135 description: 'my super description', 135 description: 'my super description',
136 thumbnailfile: 'thumbnail.jpg', 136 thumbnailfile: 'custom-thumbnail.jpg',
137 videoChannelId: servers[0].store.channel.id 137 videoChannelId: servers[0].store.channel.id
138 } 138 }
139 }) 139 })
@@ -225,7 +225,7 @@ describe('Test video playlists', function () {
225 displayName: 'my super playlist', 225 displayName: 'my super playlist',
226 privacy: VideoPlaylistPrivacy.PUBLIC, 226 privacy: VideoPlaylistPrivacy.PUBLIC,
227 description: 'my super description', 227 description: 'my super description',
228 thumbnailfile: 'thumbnail.jpg', 228 thumbnailfile: 'custom-thumbnail.jpg',
229 videoChannelId: servers[0].store.channel.id 229 videoChannelId: servers[0].store.channel.id
230 } 230 }
231 }) 231 })
@@ -286,7 +286,7 @@ describe('Test video playlists', function () {
286 attributes: { 286 attributes: {
287 displayName: 'playlist 3', 287 displayName: 'playlist 3',
288 privacy: VideoPlaylistPrivacy.PUBLIC, 288 privacy: VideoPlaylistPrivacy.PUBLIC,
289 thumbnailfile: 'thumbnail.jpg', 289 thumbnailfile: 'custom-thumbnail.jpg',
290 videoChannelId: servers[1].store.channel.id 290 videoChannelId: servers[1].store.channel.id
291 } 291 }
292 }) 292 })
@@ -314,11 +314,11 @@ describe('Test video playlists', function () {
314 314
315 const playlist2 = body.data.find(p => p.displayName === 'playlist 2') 315 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
316 expect(playlist2).to.not.be.undefined 316 expect(playlist2).to.not.be.undefined
317 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath) 317 await testImageGeneratedByFFmpeg(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
318 318
319 const playlist3 = body.data.find(p => p.displayName === 'playlist 3') 319 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
320 expect(playlist3).to.not.be.undefined 320 expect(playlist3).to.not.be.undefined
321 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath) 321 await testImageGeneratedByFFmpeg(server.url, 'custom-thumbnail', playlist3.thumbnailPath)
322 } 322 }
323 323
324 const body = await servers[2].playlists.list({ start: 0, count: 5 }) 324 const body = await servers[2].playlists.list({ start: 0, count: 5 })
@@ -336,7 +336,7 @@ describe('Test video playlists', function () {
336 336
337 const playlist2 = body.data.find(p => p.displayName === 'playlist 2') 337 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
338 expect(playlist2).to.not.be.undefined 338 expect(playlist2).to.not.be.undefined
339 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath) 339 await testImageGeneratedByFFmpeg(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
340 340
341 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined 341 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
342 }) 342 })
@@ -474,7 +474,7 @@ describe('Test video playlists', function () {
474 await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 }) 474 await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
475 }) 475 })
476 476
477 it('Should get unlisted plyaylist using uuid or shortUUID', async function () { 477 it('Should get unlisted playlist using uuid or shortUUID', async function () {
478 await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid }) 478 await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid })
479 await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID }) 479 await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID })
480 }) 480 })
@@ -502,7 +502,7 @@ describe('Test video playlists', function () {
502 displayName: 'playlist 3 updated', 502 displayName: 'playlist 3 updated',
503 description: 'description updated', 503 description: 'description updated',
504 privacy: VideoPlaylistPrivacy.UNLISTED, 504 privacy: VideoPlaylistPrivacy.UNLISTED,
505 thumbnailfile: 'thumbnail.jpg', 505 thumbnailfile: 'custom-thumbnail.jpg',
506 videoChannelId: servers[1].store.channel.id 506 videoChannelId: servers[1].store.channel.id
507 }, 507 },
508 playlistId: playlistServer2Id2 508 playlistId: playlistServer2Id2
@@ -686,7 +686,7 @@ describe('Test video playlists', function () {
686 await waitJobs(servers) 686 await waitJobs(servers)
687 }) 687 })
688 688
689 it('Should update the element type if the video is private', async function () { 689 it('Should update the element type if the video is private/password protected', async function () {
690 this.timeout(20000) 690 this.timeout(20000)
691 691
692 const name = 'video 89' 692 const name = 'video 89'
@@ -703,6 +703,19 @@ describe('Test video playlists', function () {
703 } 703 }
704 704
705 { 705 {
706 await servers[0].videos.update({
707 id: video1,
708 attributes: { privacy: VideoPrivacy.PASSWORD_PROTECTED, videoPasswords: [ 'password' ] }
709 })
710 await waitJobs(servers)
711
712 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
713 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
714 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
715 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
716 }
717
718 {
706 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } }) 719 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } })
707 await waitJobs(servers) 720 await waitJobs(servers)
708 721
diff --git a/server/tests/api/videos/video-static-file-privacy.ts b/server/tests/api/videos/video-static-file-privacy.ts
index 542848533..0a9864134 100644
--- a/server/tests/api/videos/video-static-file-privacy.ts
+++ b/server/tests/api/videos/video-static-file-privacy.ts
@@ -41,7 +41,7 @@ describe('Test video static file privacy', function () {
41 41
42 for (const file of video.files) { 42 for (const file of video.files) {
43 expect(file.fileDownloadUrl).to.not.include('/private/') 43 expect(file.fileDownloadUrl).to.not.include('/private/')
44 expectStartWith(file.fileUrl, server.url + '/static/webseed/private/') 44 expectStartWith(file.fileUrl, server.url + '/static/web-videos/private/')
45 45
46 const torrent = await parseTorrentVideo(server, file) 46 const torrent = await parseTorrentVideo(server, file)
47 expect(torrent.urlList).to.have.lengthOf(0) 47 expect(torrent.urlList).to.have.lengthOf(0)
@@ -90,7 +90,7 @@ describe('Test video static file privacy', function () {
90 } 90 }
91 } 91 }
92 92
93 it('Should upload a private/internal video and have a private static path', async function () { 93 it('Should upload a private/internal/password protected video and have a private static path', async function () {
94 this.timeout(120000) 94 this.timeout(120000)
95 95
96 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) { 96 for (const privacy of [ VideoPrivacy.PRIVATE, VideoPrivacy.INTERNAL ]) {
@@ -99,6 +99,15 @@ describe('Test video static file privacy', function () {
99 99
100 await checkPrivateFiles(uuid) 100 await checkPrivateFiles(uuid)
101 } 101 }
102
103 const { uuid } = await server.videos.quickUpload({
104 name: 'video',
105 privacy: VideoPrivacy.PASSWORD_PROTECTED,
106 videoPasswords: [ 'my super password' ]
107 })
108 await waitJobs([ server ])
109
110 await checkPrivateFiles(uuid)
102 }) 111 })
103 112
104 it('Should upload a public video and update it as private/internal to have a private static path', async function () { 113 it('Should upload a public video and update it as private/internal to have a private static path', async function () {
@@ -185,8 +194,9 @@ describe('Test video static file privacy', function () {
185 expectedStatus: HttpStatusCode 194 expectedStatus: HttpStatusCode
186 token: string 195 token: string
187 videoFileToken: string 196 videoFileToken: string
197 videoPassword?: string
188 }) { 198 }) {
189 const { id, expectedStatus, token, videoFileToken } = options 199 const { id, expectedStatus, token, videoFileToken, videoPassword } = options
190 200
191 const video = await server.videos.getWithToken({ id }) 201 const video = await server.videos.getWithToken({ id })
192 202
@@ -196,6 +206,12 @@ describe('Test video static file privacy', function () {
196 206
197 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus }) 207 await makeRawRequest({ url: file.fileUrl, query: { videoFileToken }, expectedStatus })
198 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus }) 208 await makeRawRequest({ url: file.fileDownloadUrl, query: { videoFileToken }, expectedStatus })
209
210 if (videoPassword) {
211 const headers = { 'x-peertube-video-password': videoPassword }
212 await makeRawRequest({ url: file.fileUrl, headers, expectedStatus })
213 await makeRawRequest({ url: file.fileDownloadUrl, headers, expectedStatus })
214 }
199 } 215 }
200 216
201 const hls = video.streamingPlaylists[0] 217 const hls = video.streamingPlaylists[0]
@@ -204,6 +220,12 @@ describe('Test video static file privacy', function () {
204 220
205 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus }) 221 await makeRawRequest({ url: hls.playlistUrl, query: { videoFileToken }, expectedStatus })
206 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus }) 222 await makeRawRequest({ url: hls.segmentsSha256Url, query: { videoFileToken }, expectedStatus })
223
224 if (videoPassword) {
225 const headers = { 'x-peertube-video-password': videoPassword }
226 await makeRawRequest({ url: hls.playlistUrl, token: null, headers, expectedStatus })
227 await makeRawRequest({ url: hls.segmentsSha256Url, token: null, headers, expectedStatus })
228 }
207 } 229 }
208 230
209 before(async function () { 231 before(async function () {
@@ -216,13 +238,53 @@ describe('Test video static file privacy', function () {
216 it('Should not be able to access a private video files without OAuth token and file token', async function () { 238 it('Should not be able to access a private video files without OAuth token and file token', async function () {
217 this.timeout(120000) 239 this.timeout(120000)
218 240
219 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.INTERNAL }) 241 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
220 await waitJobs([ server ]) 242 await waitJobs([ server ])
221 243
222 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null }) 244 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403, token: null, videoFileToken: null })
223 }) 245 })
224 246
225 it('Should not be able to access an internal video files without appropriate OAuth token and file token', async function () { 247 it('Should not be able to access password protected video files without OAuth token, file token and password', async function () {
248 this.timeout(120000)
249 const videoPassword = 'my super password'
250
251 const { uuid } = await server.videos.quickUpload({
252 name: 'password protected video',
253 privacy: VideoPrivacy.PASSWORD_PROTECTED,
254 videoPasswords: [ videoPassword ]
255 })
256 await waitJobs([ server ])
257
258 await checkVideoFiles({
259 id: uuid,
260 expectedStatus: HttpStatusCode.FORBIDDEN_403,
261 token: null,
262 videoFileToken: null,
263 videoPassword: null
264 })
265 })
266
267 it('Should not be able to access an password video files with incorrect OAuth token, file token and password', async function () {
268 this.timeout(120000)
269 const videoPassword = 'my super password'
270
271 const { uuid } = await server.videos.quickUpload({
272 name: 'password protected video',
273 privacy: VideoPrivacy.PASSWORD_PROTECTED,
274 videoPasswords: [ videoPassword ]
275 })
276 await waitJobs([ server ])
277
278 await checkVideoFiles({
279 id: uuid,
280 expectedStatus: HttpStatusCode.FORBIDDEN_403,
281 token: userToken,
282 videoFileToken: unrelatedFileToken,
283 videoPassword: 'incorrectPassword'
284 })
285 })
286
287 it('Should not be able to access an private video files without appropriate OAuth token and file token', async function () {
226 this.timeout(120000) 288 this.timeout(120000)
227 289
228 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE }) 290 const { uuid } = await server.videos.quickUpload({ name: 'video', privacy: VideoPrivacy.PRIVATE })
@@ -247,6 +309,23 @@ describe('Test video static file privacy', function () {
247 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken }) 309 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken })
248 }) 310 })
249 311
312 it('Should be able to access a password protected video files with appropriate OAuth token or file token', async function () {
313 this.timeout(120000)
314 const videoPassword = 'my super password'
315
316 const { uuid } = await server.videos.quickUpload({
317 name: 'video',
318 privacy: VideoPrivacy.PASSWORD_PROTECTED,
319 videoPasswords: [ videoPassword ]
320 })
321
322 const videoFileToken = await server.videoToken.getVideoFileToken({ token: null, videoId: uuid, videoPassword })
323
324 await waitJobs([ server ])
325
326 await checkVideoFiles({ id: uuid, expectedStatus: HttpStatusCode.OK_200, token: server.accessToken, videoFileToken, videoPassword })
327 })
328
250 it('Should reinject video file token', async function () { 329 it('Should reinject video file token', async function () {
251 this.timeout(120000) 330 this.timeout(120000)
252 331
@@ -294,13 +373,20 @@ describe('Test video static file privacy', function () {
294 let permanentLiveId: string 373 let permanentLiveId: string
295 let permanentLive: LiveVideo 374 let permanentLive: LiveVideo
296 375
376 let passwordProtectedLiveId: string
377 let passwordProtectedLive: LiveVideo
378
379 const correctPassword = 'my super password'
380
297 let unrelatedFileToken: string 381 let unrelatedFileToken: string
298 382
299 async function checkLiveFiles (live: LiveVideo, liveId: string) { 383 async function checkLiveFiles (options: { live: LiveVideo, liveId: string, videoPassword?: string }) {
384 const { live, liveId, videoPassword } = options
300 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey }) 385 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
301 await server.live.waitUntilPublished({ videoId: liveId }) 386 await server.live.waitUntilPublished({ videoId: liveId })
302 387
303 const video = await server.videos.getWithToken({ id: liveId }) 388 const video = await server.videos.getWithToken({ id: liveId })
389
304 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid }) 390 const fileToken = await server.videoToken.getVideoFileToken({ videoId: video.uuid })
305 391
306 const hls = video.streamingPlaylists[0] 392 const hls = video.streamingPlaylists[0]
@@ -314,6 +400,16 @@ describe('Test video static file privacy', function () {
314 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 400 await makeRawRequest({ url, token: userToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
315 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 401 await makeRawRequest({ url, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
316 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) 402 await makeRawRequest({ url, query: { videoFileToken: unrelatedFileToken }, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
403
404 if (videoPassword) {
405 await makeRawRequest({ url, headers: { 'x-peertube-video-password': videoPassword }, expectedStatus: HttpStatusCode.OK_200 })
406 await makeRawRequest({
407 url,
408 headers: { 'x-peertube-video-password': 'incorrectPassword' },
409 expectedStatus: HttpStatusCode.FORBIDDEN_403
410 })
411 }
412
317 } 413 }
318 414
319 await stopFfmpeg(ffmpegCommand) 415 await stopFfmpeg(ffmpegCommand)
@@ -381,18 +477,35 @@ describe('Test video static file privacy', function () {
381 permanentLiveId = video.uuid 477 permanentLiveId = video.uuid
382 permanentLive = live 478 permanentLive = live
383 } 479 }
480
481 {
482 const { video, live } = await server.live.quickCreate({
483 saveReplay: false,
484 permanentLive: false,
485 privacy: VideoPrivacy.PASSWORD_PROTECTED,
486 videoPasswords: [ correctPassword ]
487 })
488 passwordProtectedLiveId = video.uuid
489 passwordProtectedLive = live
490 }
384 }) 491 })
385 492
386 it('Should create a private normal live and have a private static path', async function () { 493 it('Should create a private normal live and have a private static path', async function () {
387 this.timeout(240000) 494 this.timeout(240000)
388 495
389 await checkLiveFiles(normalLive, normalLiveId) 496 await checkLiveFiles({ live: normalLive, liveId: normalLiveId })
390 }) 497 })
391 498
392 it('Should create a private permanent live and have a private static path', async function () { 499 it('Should create a private permanent live and have a private static path', async function () {
393 this.timeout(240000) 500 this.timeout(240000)
394 501
395 await checkLiveFiles(permanentLive, permanentLiveId) 502 await checkLiveFiles({ live: permanentLive, liveId: permanentLiveId })
503 })
504
505 it('Should create a password protected live and have a private static path', async function () {
506 this.timeout(240000)
507
508 await checkLiveFiles({ live: passwordProtectedLive, liveId: passwordProtectedLiveId, videoPassword: correctPassword })
396 }) 509 })
397 510
398 it('Should reinject video file token on permanent live', async function () { 511 it('Should reinject video file token on permanent live', async function () {
diff --git a/server/tests/api/videos/video-storyboard.ts b/server/tests/api/videos/video-storyboard.ts
new file mode 100644
index 000000000..fc4b4450f
--- /dev/null
+++ b/server/tests/api/videos/video-storyboard.ts
@@ -0,0 +1,213 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { expect } from 'chai'
4import { readdir } from 'fs-extra'
5import { basename } from 'path'
6import { FIXTURE_URLS } from '@server/tests/shared'
7import { areHttpImportTestsDisabled } from '@shared/core-utils'
8import { HttpStatusCode, VideoPrivacy } from '@shared/models'
9import {
10 cleanupTests,
11 createMultipleServers,
12 doubleFollow,
13 makeGetRequest,
14 PeerTubeServer,
15 sendRTMPStream,
16 setAccessTokensToServers,
17 setDefaultVideoChannel,
18 stopFfmpeg,
19 waitJobs
20} from '@shared/server-commands'
21
22async function checkStoryboard (options: {
23 server: PeerTubeServer
24 uuid: string
25 tilesCount?: number
26 minSize?: number
27}) {
28 const { server, uuid, tilesCount, minSize = 1000 } = options
29
30 const { storyboards } = await server.storyboard.list({ id: uuid })
31
32 expect(storyboards).to.have.lengthOf(1)
33
34 const storyboard = storyboards[0]
35
36 expect(storyboard.spriteDuration).to.equal(1)
37 expect(storyboard.spriteHeight).to.equal(108)
38 expect(storyboard.spriteWidth).to.equal(192)
39 expect(storyboard.storyboardPath).to.exist
40
41 if (tilesCount) {
42 expect(storyboard.totalWidth).to.equal(192 * Math.min(tilesCount, 10))
43 expect(storyboard.totalHeight).to.equal(108 * Math.max((tilesCount / 10), 1))
44 }
45
46 const { body } = await makeGetRequest({ url: server.url, path: storyboard.storyboardPath, expectedStatus: HttpStatusCode.OK_200 })
47 expect(body.length).to.be.above(minSize)
48}
49
50describe('Test video storyboard', function () {
51 let servers: PeerTubeServer[]
52
53 let baseUUID: string
54
55 before(async function () {
56 this.timeout(120000)
57
58 servers = await createMultipleServers(2)
59 await setAccessTokensToServers(servers)
60 await setDefaultVideoChannel(servers)
61
62 await doubleFollow(servers[0], servers[1])
63 })
64
65 it('Should generate a storyboard after upload without transcoding', async function () {
66 this.timeout(60000)
67
68 // 5s video
69 const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_short.webm' })
70 baseUUID = uuid
71 await waitJobs(servers)
72
73 for (const server of servers) {
74 await checkStoryboard({ server, uuid, tilesCount: 5 })
75 }
76 })
77
78 it('Should generate a storyboard after upload without transcoding with a long video', async function () {
79 this.timeout(60000)
80
81 // 124s video
82 const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_very_long_10p.mp4' })
83 await waitJobs(servers)
84
85 for (const server of servers) {
86 await checkStoryboard({ server, uuid, tilesCount: 100 })
87 }
88 })
89
90 it('Should generate a storyboard after upload with transcoding', async function () {
91 this.timeout(60000)
92
93 await servers[0].config.enableMinimumTranscoding()
94
95 // 5s video
96 const { uuid } = await servers[0].videos.quickUpload({ name: 'upload', fixture: 'video_short.webm' })
97 await waitJobs(servers)
98
99 for (const server of servers) {
100 await checkStoryboard({ server, uuid, tilesCount: 5 })
101 }
102 })
103
104 it('Should generate a storyboard after an audio upload', async function () {
105 this.timeout(60000)
106
107 // 6s audio
108 const attributes = { name: 'audio', fixture: 'sample.ogg' }
109 const { uuid } = await servers[0].videos.upload({ attributes, mode: 'legacy' })
110 await waitJobs(servers)
111
112 for (const server of servers) {
113 try {
114 await checkStoryboard({ server, uuid, tilesCount: 6, minSize: 250 })
115 } catch { // FIXME: to remove after ffmpeg CI upgrade, ffmpeg CI version (4.3) generates a 7.6s length video
116 await checkStoryboard({ server, uuid, tilesCount: 8, minSize: 250 })
117 }
118 }
119 })
120
121 it('Should generate a storyboard after HTTP import', async function () {
122 this.timeout(60000)
123
124 if (areHttpImportTestsDisabled()) return
125
126 // 3s video
127 const { video } = await servers[0].imports.importVideo({
128 attributes: {
129 targetUrl: FIXTURE_URLS.goodVideo,
130 channelId: servers[0].store.channel.id,
131 privacy: VideoPrivacy.PUBLIC
132 }
133 })
134 await waitJobs(servers)
135
136 for (const server of servers) {
137 await checkStoryboard({ server, uuid: video.uuid, tilesCount: 3 })
138 }
139 })
140
141 it('Should generate a storyboard after torrent import', async function () {
142 this.timeout(60000)
143
144 if (areHttpImportTestsDisabled()) return
145
146 // 10s video
147 const { video } = await servers[0].imports.importVideo({
148 attributes: {
149 magnetUri: FIXTURE_URLS.magnet,
150 channelId: servers[0].store.channel.id,
151 privacy: VideoPrivacy.PUBLIC
152 }
153 })
154 await waitJobs(servers)
155
156 for (const server of servers) {
157 await checkStoryboard({ server, uuid: video.uuid, tilesCount: 10 })
158 }
159 })
160
161 it('Should generate a storyboard after a live', async function () {
162 this.timeout(240000)
163
164 await servers[0].config.enableLive({ allowReplay: true, transcoding: true, resolutions: 'min' })
165
166 const { live, video } = await servers[0].live.quickCreate({
167 saveReplay: true,
168 permanentLive: false,
169 privacy: VideoPrivacy.PUBLIC
170 })
171
172 const ffmpegCommand = sendRTMPStream({ rtmpBaseUrl: live.rtmpUrl, streamKey: live.streamKey })
173 await servers[0].live.waitUntilPublished({ videoId: video.id })
174
175 await stopFfmpeg(ffmpegCommand)
176
177 await servers[0].live.waitUntilReplacedByReplay({ videoId: video.id })
178 await waitJobs(servers)
179
180 for (const server of servers) {
181 await checkStoryboard({ server, uuid: video.uuid })
182 }
183 })
184
185 it('Should cleanup storyboards on video deletion', async function () {
186 this.timeout(60000)
187
188 const { storyboards } = await servers[0].storyboard.list({ id: baseUUID })
189 const storyboardName = basename(storyboards[0].storyboardPath)
190
191 const listFiles = () => {
192 const storyboardPath = servers[0].getDirectoryPath('storyboards')
193 return readdir(storyboardPath)
194 }
195
196 {
197 const storyboads = await listFiles()
198 expect(storyboads).to.include(storyboardName)
199 }
200
201 await servers[0].videos.remove({ id: baseUUID })
202 await waitJobs(servers)
203
204 {
205 const storyboads = await listFiles()
206 expect(storyboads).to.not.include(storyboardName)
207 }
208 })
209
210 after(async function () {
211 await cleanupTests(servers)
212 })
213})
diff --git a/server/tests/api/videos/videos-common-filters.ts b/server/tests/api/videos/videos-common-filters.ts
index 30251706b..73c066bfb 100644
--- a/server/tests/api/videos/videos-common-filters.ts
+++ b/server/tests/api/videos/videos-common-filters.ts
@@ -154,7 +154,7 @@ describe('Test videos filter', function () {
154 server: PeerTubeServer 154 server: PeerTubeServer
155 path: string 155 path: string
156 isLocal?: boolean 156 isLocal?: boolean
157 hasWebtorrentFiles?: boolean 157 hasWebVideoFiles?: boolean
158 hasHLSFiles?: boolean 158 hasHLSFiles?: boolean
159 include?: VideoInclude 159 include?: VideoInclude
160 privacyOneOf?: VideoPrivacy[] 160 privacyOneOf?: VideoPrivacy[]
@@ -174,7 +174,7 @@ describe('Test videos filter', function () {
174 'include', 174 'include',
175 'category', 175 'category',
176 'tagsAllOf', 176 'tagsAllOf',
177 'hasWebtorrentFiles', 177 'hasWebVideoFiles',
178 'hasHLSFiles', 178 'hasHLSFiles',
179 'privacyOneOf', 179 'privacyOneOf',
180 'excludeAlreadyWatched' 180 'excludeAlreadyWatched'
@@ -463,14 +463,14 @@ describe('Test videos filter', function () {
463 } 463 }
464 }) 464 })
465 465
466 it('Should filter by HLS or WebTorrent files', async function () { 466 it('Should filter by HLS or Web Video files', async function () {
467 this.timeout(360000) 467 this.timeout(360000)
468 468
469 const finderFactory = (name: string) => (videos: Video[]) => videos.some(v => v.name === name) 469 const finderFactory = (name: string) => (videos: Video[]) => videos.some(v => v.name === name)
470 470
471 await servers[0].config.enableTranscoding(true, false) 471 await servers[0].config.enableTranscoding(true, false)
472 await servers[0].videos.upload({ attributes: { name: 'webtorrent video' } }) 472 await servers[0].videos.upload({ attributes: { name: 'web video video' } })
473 const hasWebtorrent = finderFactory('webtorrent video') 473 const hasWebVideo = finderFactory('web video video')
474 474
475 await waitJobs(servers) 475 await waitJobs(servers)
476 476
@@ -481,24 +481,24 @@ describe('Test videos filter', function () {
481 await waitJobs(servers) 481 await waitJobs(servers)
482 482
483 await servers[0].config.enableTranscoding(true, true) 483 await servers[0].config.enableTranscoding(true, true)
484 await servers[0].videos.upload({ attributes: { name: 'hls and webtorrent video' } }) 484 await servers[0].videos.upload({ attributes: { name: 'hls and web video video' } })
485 const hasBoth = finderFactory('hls and webtorrent video') 485 const hasBoth = finderFactory('hls and web video video')
486 486
487 await waitJobs(servers) 487 await waitJobs(servers)
488 488
489 for (const path of paths) { 489 for (const path of paths) {
490 { 490 {
491 const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: true }) 491 const videos = await listVideos({ server: servers[0], path, hasWebVideoFiles: true })
492 492
493 expect(hasWebtorrent(videos)).to.be.true 493 expect(hasWebVideo(videos)).to.be.true
494 expect(hasHLS(videos)).to.be.false 494 expect(hasHLS(videos)).to.be.false
495 expect(hasBoth(videos)).to.be.true 495 expect(hasBoth(videos)).to.be.true
496 } 496 }
497 497
498 { 498 {
499 const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: false }) 499 const videos = await listVideos({ server: servers[0], path, hasWebVideoFiles: false })
500 500
501 expect(hasWebtorrent(videos)).to.be.false 501 expect(hasWebVideo(videos)).to.be.false
502 expect(hasHLS(videos)).to.be.true 502 expect(hasHLS(videos)).to.be.true
503 expect(hasBoth(videos)).to.be.false 503 expect(hasBoth(videos)).to.be.false
504 } 504 }
@@ -506,7 +506,7 @@ describe('Test videos filter', function () {
506 { 506 {
507 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true }) 507 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true })
508 508
509 expect(hasWebtorrent(videos)).to.be.false 509 expect(hasWebVideo(videos)).to.be.false
510 expect(hasHLS(videos)).to.be.true 510 expect(hasHLS(videos)).to.be.true
511 expect(hasBoth(videos)).to.be.true 511 expect(hasBoth(videos)).to.be.true
512 } 512 }
@@ -514,23 +514,23 @@ describe('Test videos filter', function () {
514 { 514 {
515 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false }) 515 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false })
516 516
517 expect(hasWebtorrent(videos)).to.be.true 517 expect(hasWebVideo(videos)).to.be.true
518 expect(hasHLS(videos)).to.be.false 518 expect(hasHLS(videos)).to.be.false
519 expect(hasBoth(videos)).to.be.false 519 expect(hasBoth(videos)).to.be.false
520 } 520 }
521 521
522 { 522 {
523 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false, hasWebtorrentFiles: false }) 523 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false, hasWebVideoFiles: false })
524 524
525 expect(hasWebtorrent(videos)).to.be.false 525 expect(hasWebVideo(videos)).to.be.false
526 expect(hasHLS(videos)).to.be.false 526 expect(hasHLS(videos)).to.be.false
527 expect(hasBoth(videos)).to.be.false 527 expect(hasBoth(videos)).to.be.false
528 } 528 }
529 529
530 { 530 {
531 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true, hasWebtorrentFiles: true }) 531 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true, hasWebVideoFiles: true })
532 532
533 expect(hasWebtorrent(videos)).to.be.false 533 expect(hasWebVideo(videos)).to.be.false
534 expect(hasHLS(videos)).to.be.false 534 expect(hasHLS(videos)).to.be.false
535 expect(hasBoth(videos)).to.be.true 535 expect(hasBoth(videos)).to.be.true
536 } 536 }