aboutsummaryrefslogtreecommitdiffhomepage
path: root/packages/tests/src/api
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2023-08-28 10:55:04 +0200
committerChocobozzz <me@florianbigard.com>2023-08-28 16:17:31 +0200
commit77b70702d2193d78bf6fbd07f0fc7335e34957f8 (patch)
tree1a0aed540054286c9a8b10c4890cc0f718e00458 /packages/tests/src/api
parent7113f32a87bd6b2868154fed20bde1a1633c190e (diff)
downloadPeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.gz
PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.zst
PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.zip
Add video chapters support
Diffstat (limited to 'packages/tests/src/api')
-rw-r--r--packages/tests/src/api/check-params/index.ts1
-rw-r--r--packages/tests/src/api/check-params/video-captions.ts23
-rw-r--r--packages/tests/src/api/check-params/video-chapters.ts172
-rw-r--r--packages/tests/src/api/videos/index.ts1
-rw-r--r--packages/tests/src/api/videos/video-chapters.ts342
5 files changed, 530 insertions, 9 deletions
diff --git a/packages/tests/src/api/check-params/index.ts b/packages/tests/src/api/check-params/index.ts
index ed5fe6b06..d7867e8a5 100644
--- a/packages/tests/src/api/check-params/index.ts
+++ b/packages/tests/src/api/check-params/index.ts
@@ -30,6 +30,7 @@ import './video-blacklist.js'
30import './video-captions.js' 30import './video-captions.js'
31import './video-channel-syncs.js' 31import './video-channel-syncs.js'
32import './video-channels.js' 32import './video-channels.js'
33import './video-chapters.js'
33import './video-comments.js' 34import './video-comments.js'
34import './video-files.js' 35import './video-files.js'
35import './video-imports.js' 36import './video-imports.js'
diff --git a/packages/tests/src/api/check-params/video-captions.ts b/packages/tests/src/api/check-params/video-captions.ts
index 4150b095f..ac4e85068 100644
--- a/packages/tests/src/api/check-params/video-captions.ts
+++ b/packages/tests/src/api/check-params/video-captions.ts
@@ -31,15 +31,7 @@ describe('Test video captions API validator', function () {
31 31
32 video = await server.videos.upload() 32 video = await server.videos.upload()
33 privateVideo = await server.videos.upload({ attributes: { privacy: VideoPrivacy.PRIVATE } }) 33 privateVideo = await server.videos.upload({ attributes: { privacy: VideoPrivacy.PRIVATE } })
34 34 userAccessToken = await server.users.generateUserAndToken('user1')
35 {
36 const user = {
37 username: 'user1',
38 password: 'my super password'
39 }
40 await server.users.create({ username: user.username, password: user.password })
41 userAccessToken = await server.login.getAccessToken(user)
42 }
43 }) 35 })
44 36
45 describe('When adding video caption', function () { 37 describe('When adding video caption', function () {
@@ -120,6 +112,19 @@ describe('Test video captions API validator', function () {
120 }) 112 })
121 }) 113 })
122 114
115 it('Should fail with another user token', async function () {
116 const captionPath = path + video.uuid + '/captions/fr'
117 await makeUploadRequest({
118 method: 'PUT',
119 url: server.url,
120 path: captionPath,
121 token: userAccessToken,
122 fields,
123 attaches,
124 expectedStatus: HttpStatusCode.FORBIDDEN_403
125 })
126 })
127
123 // We accept any file now 128 // We accept any file now
124 // it('Should fail with an invalid captionfile extension', async function () { 129 // it('Should fail with an invalid captionfile extension', async function () {
125 // const attaches = { 130 // const attaches = {
diff --git a/packages/tests/src/api/check-params/video-chapters.ts b/packages/tests/src/api/check-params/video-chapters.ts
new file mode 100644
index 000000000..c59f88e79
--- /dev/null
+++ b/packages/tests/src/api/check-params/video-chapters.ts
@@ -0,0 +1,172 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { HttpStatusCode, Video, VideoCreateResult, VideoPrivacy } from '@peertube/peertube-models'
4import {
5 PeerTubeServer,
6 cleanupTests,
7 createSingleServer,
8 setAccessTokensToServers,
9 setDefaultVideoChannel
10} from '@peertube/peertube-server-commands'
11
12describe('Test videos chapters API validator', function () {
13 let server: PeerTubeServer
14 let video: VideoCreateResult
15 let live: Video
16 let privateVideo: VideoCreateResult
17 let userAccessToken: string
18
19 // ---------------------------------------------------------------
20
21 before(async function () {
22 this.timeout(30000)
23
24 server = await createSingleServer(1)
25
26 await setAccessTokensToServers([ server ])
27 await setDefaultVideoChannel([ server ])
28
29 video = await server.videos.upload()
30 privateVideo = await server.videos.upload({ attributes: { privacy: VideoPrivacy.PRIVATE } })
31 userAccessToken = await server.users.generateUserAndToken('user1')
32
33 await server.config.enableLive({ allowReplay: false })
34
35 const res = await server.live.quickCreate({ saveReplay: false, permanentLive: false })
36 live = res.video
37 })
38
39 describe('When updating chapters', function () {
40
41 it('Should fail without a valid uuid', async function () {
42 await server.chapters.update({ videoId: '4da6fd', chapters: [], expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
43 })
44
45 it('Should fail with an unknown id', async function () {
46 await server.chapters.update({
47 videoId: 'ce0801ef-7124-48df-9b22-b473ace78797',
48 chapters: [],
49 expectedStatus: HttpStatusCode.NOT_FOUND_404
50 })
51 })
52
53 it('Should fail without access token', async function () {
54 await server.chapters.update({
55 videoId: video.id,
56 chapters: [],
57 token: null,
58 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
59 })
60 })
61
62 it('Should fail with a bad access token', async function () {
63 await server.chapters.update({
64 videoId: video.id,
65 chapters: [],
66 token: 'toto',
67 expectedStatus: HttpStatusCode.UNAUTHORIZED_401
68 })
69 })
70
71 it('Should fail with a another user access token', async function () {
72 await server.chapters.update({
73 videoId: video.id,
74 chapters: [],
75 token: userAccessToken,
76 expectedStatus: HttpStatusCode.FORBIDDEN_403
77 })
78 })
79
80 it('Should fail with a wrong chapters param', async function () {
81 await server.chapters.update({
82 videoId: video.id,
83 chapters: 'hello' as any,
84 expectedStatus: HttpStatusCode.BAD_REQUEST_400
85 })
86 })
87
88 it('Should fail with a bad chapter title', async function () {
89 await server.chapters.update({
90 videoId: video.id,
91 chapters: [ { title: 'hello', timecode: 21 }, { title: '', timecode: 21 } ],
92 expectedStatus: HttpStatusCode.BAD_REQUEST_400
93 })
94
95 await server.chapters.update({
96 videoId: video.id,
97 chapters: [ { title: 'hello', timecode: 21 }, { title: 'a'.repeat(150), timecode: 21 } ],
98 expectedStatus: HttpStatusCode.BAD_REQUEST_400
99 })
100 })
101
102 it('Should fail with a bad timecode', async function () {
103 await server.chapters.update({
104 videoId: video.id,
105 chapters: [ { title: 'hello', timecode: 21 }, { title: 'title', timecode: -5 } ],
106 expectedStatus: HttpStatusCode.BAD_REQUEST_400
107 })
108
109 await server.chapters.update({
110 videoId: video.id,
111 chapters: [ { title: 'hello', timecode: 21 }, { title: 'title', timecode: 'hi' as any } ],
112 expectedStatus: HttpStatusCode.BAD_REQUEST_400
113 })
114 })
115
116 it('Should fail with non unique timecodes', async function () {
117 await server.chapters.update({
118 videoId: video.id,
119 chapters: [ { title: 'hello', timecode: 21 }, { title: 'title', timecode: 22 }, { title: 'hello', timecode: 21 } ],
120 expectedStatus: HttpStatusCode.BAD_REQUEST_400
121 })
122 })
123
124 it('Should fail to create chapters on a live', async function () {
125 await server.chapters.update({
126 videoId: live.id,
127 chapters: [],
128 expectedStatus: HttpStatusCode.BAD_REQUEST_400
129 })
130 })
131
132 it('Should succeed with the correct params', async function () {
133 await server.chapters.update({
134 videoId: video.id,
135 chapters: []
136 })
137
138 await server.chapters.update({
139 videoId: video.id,
140 chapters: [ { title: 'hello', timecode: 21 }, { title: 'hello 2', timecode: 35 } ]
141 })
142 })
143 })
144
145 describe('When listing chapters', function () {
146
147 it('Should fail without a valid uuid', async function () {
148 await server.chapters.list({ videoId: '4da6fd', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
149 })
150
151 it('Should fail with an unknown id', async function () {
152 await server.chapters.list({ videoId: '4da6fde3-88f7-4d16-b119-108df5630b06', expectedStatus: HttpStatusCode.NOT_FOUND_404 })
153 })
154
155 it('Should not list private chapters to anyone', async function () {
156 await server.chapters.list({ videoId: privateVideo.uuid, token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
157 })
158
159 it('Should not list private chapters to another user', async function () {
160 await server.chapters.list({ videoId: privateVideo.uuid, token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
161 })
162
163 it('Should list chapters', async function () {
164 await server.chapters.list({ videoId: privateVideo.uuid })
165 await server.chapters.list({ videoId: video.uuid })
166 })
167 })
168
169 after(async function () {
170 await cleanupTests([ server ])
171 })
172})
diff --git a/packages/tests/src/api/videos/index.ts b/packages/tests/src/api/videos/index.ts
index fcb1d5a81..a4bcd9741 100644
--- a/packages/tests/src/api/videos/index.ts
+++ b/packages/tests/src/api/videos/index.ts
@@ -4,6 +4,7 @@ import './single-server.js'
4import './video-captions.js' 4import './video-captions.js'
5import './video-change-ownership.js' 5import './video-change-ownership.js'
6import './video-channels.js' 6import './video-channels.js'
7import './video-chapters.js'
7import './channel-import-videos.js' 8import './channel-import-videos.js'
8import './video-channel-syncs.js' 9import './video-channel-syncs.js'
9import './video-comments.js' 10import './video-comments.js'
diff --git a/packages/tests/src/api/videos/video-chapters.ts b/packages/tests/src/api/videos/video-chapters.ts
new file mode 100644
index 000000000..2f3dbcd2e
--- /dev/null
+++ b/packages/tests/src/api/videos/video-chapters.ts
@@ -0,0 +1,342 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { VideoChapter, VideoCreateResult, VideoPrivacy } from '@peertube/peertube-models'
4import { areHttpImportTestsDisabled } from '@peertube/peertube-node-utils'
5import {
6 cleanupTests,
7 createMultipleServers,
8 doubleFollow, PeerTubeServer, setAccessTokensToServers,
9 setDefaultVideoChannel,
10 waitJobs
11} from '@peertube/peertube-server-commands'
12import { FIXTURE_URLS } from '@tests/shared/tests.js'
13import { expect } from 'chai'
14
15describe('Test video chapters', function () {
16 let servers: PeerTubeServer[]
17
18 before(async function () {
19 this.timeout(120000)
20
21 servers = await createMultipleServers(2)
22 await setAccessTokensToServers(servers)
23 await setDefaultVideoChannel(servers)
24
25 await doubleFollow(servers[0], servers[1])
26 })
27
28 describe('Common tests', function () {
29 let video: VideoCreateResult
30
31 before(async function () {
32 this.timeout(120000)
33
34 video = await servers[0].videos.quickUpload({ name: 'video' })
35 await waitJobs(servers)
36 })
37
38 it('Should not have chapters', async function () {
39 for (const server of servers) {
40 const { chapters } = await server.chapters.list({ videoId: video.uuid })
41
42 expect(chapters).to.deep.equal([])
43 }
44 })
45
46 it('Should set chaptets', async function () {
47 await servers[0].chapters.update({
48 videoId: video.uuid,
49 chapters: [
50 { title: 'chapter 1', timecode: 45 },
51 { title: 'chapter 2', timecode: 58 }
52 ]
53 })
54 await waitJobs(servers)
55
56 for (const server of servers) {
57 const { chapters } = await server.chapters.list({ videoId: video.uuid })
58
59 expect(chapters).to.deep.equal([
60 { title: 'chapter 1', timecode: 45 },
61 { title: 'chapter 2', timecode: 58 }
62 ])
63 }
64 })
65
66 it('Should add new chapters', async function () {
67 await servers[0].chapters.update({
68 videoId: video.uuid,
69 chapters: [
70 { title: 'chapter 1', timecode: 45 },
71 { title: 'chapter 2', timecode: 46 },
72 { title: 'chapter 3', timecode: 58 }
73 ]
74 })
75 await waitJobs(servers)
76
77 for (const server of servers) {
78 const { chapters } = await server.chapters.list({ videoId: video.uuid })
79
80 expect(chapters).to.deep.equal([
81 { title: 'chapter 1', timecode: 45 },
82 { title: 'chapter 2', timecode: 46 },
83 { title: 'chapter 3', timecode: 58 }
84 ])
85 }
86 })
87
88 it('Should delete all chapters', async function () {
89 await servers[0].chapters.update({ videoId: video.uuid, chapters: [] })
90 await waitJobs(servers)
91
92 for (const server of servers) {
93 const { chapters } = await server.chapters.list({ videoId: video.uuid })
94
95 expect(chapters).to.deep.equal([])
96 }
97 })
98 })
99
100 describe('With chapters in description', function () {
101 const description = 'this is a super description\n' +
102 '00:00 chapter 1\n' +
103 '00:03 chapter 2\n' +
104 '00:04 chapter 3\n'
105
106 function checkChapters (chapters: VideoChapter[]) {
107 expect(chapters).to.deep.equal([
108 {
109 timecode: 0,
110 title: 'chapter 1'
111 },
112 {
113 timecode: 3,
114 title: 'chapter 2'
115 },
116 {
117 timecode: 4,
118 title: 'chapter 3'
119 }
120 ])
121 }
122
123 it('Should upload a video with chapters in description', async function () {
124 const video = await servers[0].videos.upload({ attributes: { name: 'description', description } })
125 await waitJobs(servers)
126
127 for (const server of servers) {
128 const { chapters } = await server.chapters.list({ videoId: video.uuid })
129
130 checkChapters(chapters)
131 }
132 })
133
134 it('Should update a video description and automatically add chapters', async function () {
135 const video = await servers[0].videos.quickUpload({ name: 'update description' })
136 await waitJobs(servers)
137
138 for (const server of servers) {
139 const { chapters } = await server.chapters.list({ videoId: video.uuid })
140
141 expect(chapters).to.deep.equal([])
142 }
143
144 await servers[0].videos.update({ id: video.uuid, attributes: { description } })
145 await waitJobs(servers)
146
147 for (const server of servers) {
148 const { chapters } = await server.chapters.list({ videoId: video.uuid })
149
150 checkChapters(chapters)
151 }
152 })
153
154 it('Should update a video description but not automatically add chapters since the video already has chapters', async function () {
155 const video = await servers[0].videos.quickUpload({ name: 'update description' })
156
157 await servers[0].chapters.update({ videoId: video.uuid, chapters: [ { timecode: 5, title: 'chapter 1' } ] })
158 await servers[0].videos.update({ id: video.uuid, attributes: { description } })
159
160 await waitJobs(servers)
161
162 for (const server of servers) {
163 const { chapters } = await server.chapters.list({ videoId: video.uuid })
164
165 expect(chapters).to.deep.equal([ { timecode: 5, title: 'chapter 1' } ])
166 }
167 })
168
169 it('Should update multiple times chapters from description', async function () {
170 const video = await servers[0].videos.quickUpload({ name: 'update description' })
171
172 await servers[0].videos.update({ id: video.uuid, attributes: { description } })
173 await waitJobs(servers)
174
175 for (const server of servers) {
176 const { chapters } = await server.chapters.list({ videoId: video.uuid })
177
178 checkChapters(chapters)
179 }
180
181 await servers[0].videos.update({ id: video.uuid, attributes: { description: '00:01 chapter 1' } })
182 await waitJobs(servers)
183
184 for (const server of servers) {
185 const { chapters } = await server.chapters.list({ videoId: video.uuid })
186
187 expect(chapters).to.deep.equal([ { timecode: 1, title: 'chapter 1' } ])
188 }
189
190 await servers[0].videos.update({ id: video.uuid, attributes: { description: 'null description' } })
191 await waitJobs(servers)
192
193 for (const server of servers) {
194 const { chapters } = await server.chapters.list({ videoId: video.uuid })
195
196 expect(chapters).to.deep.equal([])
197 }
198 })
199 })
200
201 describe('With upload', function () {
202
203 it('Should upload a mp4 containing chapters and automatically add them', async function () {
204 const video = await servers[0].videos.quickUpload({ fixture: 'video_chapters.mp4', name: 'chapters' })
205 await waitJobs(servers)
206
207 for (const server of servers) {
208 const { chapters } = await server.chapters.list({ videoId: video.uuid })
209
210 expect(chapters).to.deep.equal([
211 {
212 timecode: 0,
213 title: 'Chapter 1'
214 },
215 {
216 timecode: 2,
217 title: 'Chapter 2'
218 },
219 {
220 timecode: 4,
221 title: 'Chapter 3'
222 }
223 ])
224 }
225 })
226 })
227
228 describe('With URL import', function () {
229 if (areHttpImportTestsDisabled()) return
230
231 it('Should detect chapters from youtube URL import', async function () {
232 this.timeout(120000)
233
234 const attributes = {
235 channelId: servers[0].store.channel.id,
236 privacy: VideoPrivacy.PUBLIC,
237 targetUrl: FIXTURE_URLS.youtubeChapters,
238 description: 'this is a super description\n'
239 }
240 const { video } = await servers[0].imports.importVideo({ attributes })
241
242 await waitJobs(servers)
243
244 for (const server of servers) {
245 const { chapters } = await server.chapters.list({ videoId: video.uuid })
246
247 expect(chapters).to.deep.equal([
248 {
249 timecode: 0,
250 title: 'chapter 1'
251 },
252 {
253 timecode: 15,
254 title: 'chapter 2'
255 },
256 {
257 timecode: 35,
258 title: 'chapter 3'
259 },
260 {
261 timecode: 40,
262 title: 'chapter 4'
263 }
264 ])
265 }
266 })
267
268 it('Should have overriden description priority from youtube URL import', async function () {
269 this.timeout(120000)
270
271 const attributes = {
272 channelId: servers[0].store.channel.id,
273 privacy: VideoPrivacy.PUBLIC,
274 targetUrl: FIXTURE_URLS.youtubeChapters,
275 description: 'this is a super description\n' +
276 '00:00 chapter 1\n' +
277 '00:03 chapter 2\n' +
278 '00:04 chapter 3\n'
279 }
280 const { video } = await servers[0].imports.importVideo({ attributes })
281
282 await waitJobs(servers)
283
284 for (const server of servers) {
285 const { chapters } = await server.chapters.list({ videoId: video.uuid })
286
287 expect(chapters).to.deep.equal([
288 {
289 timecode: 0,
290 title: 'chapter 1'
291 },
292 {
293 timecode: 3,
294 title: 'chapter 2'
295 },
296 {
297 timecode: 4,
298 title: 'chapter 3'
299 }
300 ])
301 }
302 })
303
304 it('Should detect chapters from raw URL import', async function () {
305 this.timeout(120000)
306
307 const attributes = {
308 channelId: servers[0].store.channel.id,
309 privacy: VideoPrivacy.PUBLIC,
310 targetUrl: FIXTURE_URLS.chatersVideo
311 }
312 const { video } = await servers[0].imports.importVideo({ attributes })
313
314 await waitJobs(servers)
315
316 for (const server of servers) {
317 const { chapters } = await server.chapters.list({ videoId: video.uuid })
318
319 expect(chapters).to.deep.equal([
320 {
321 timecode: 0,
322 title: 'Chapter 1'
323 },
324 {
325 timecode: 2,
326 title: 'Chapter 2'
327 },
328 {
329 timecode: 4,
330 title: 'Chapter 3'
331 }
332 ])
333 }
334 })
335 })
336
337 // TODO: test torrent import too
338
339 after(async function () {
340 await cleanupTests(servers)
341 })
342})