diff options
author | Chocobozzz <me@florianbigard.com> | 2023-08-28 10:55:04 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-08-28 16:17:31 +0200 |
commit | 77b70702d2193d78bf6fbd07f0fc7335e34957f8 (patch) | |
tree | 1a0aed540054286c9a8b10c4890cc0f718e00458 /packages/tests | |
parent | 7113f32a87bd6b2868154fed20bde1a1633c190e (diff) | |
download | PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.gz PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.tar.zst PeerTube-77b70702d2193d78bf6fbd07f0fc7335e34957f8.zip |
Add video chapters support
Diffstat (limited to 'packages/tests')
-rw-r--r-- | packages/tests/fixtures/video_chapters.mp4 | bin | 0 -> 39611 bytes | |||
-rw-r--r-- | packages/tests/src/api/check-params/index.ts | 1 | ||||
-rw-r--r-- | packages/tests/src/api/check-params/video-captions.ts | 23 | ||||
-rw-r--r-- | packages/tests/src/api/check-params/video-chapters.ts | 172 | ||||
-rw-r--r-- | packages/tests/src/api/videos/index.ts | 1 | ||||
-rw-r--r-- | packages/tests/src/api/videos/video-chapters.ts | 342 | ||||
-rw-r--r-- | packages/tests/src/server-helpers/core-utils.ts | 27 | ||||
-rw-r--r-- | packages/tests/src/shared/tests.ts | 3 |
8 files changed, 559 insertions, 10 deletions
diff --git a/packages/tests/fixtures/video_chapters.mp4 b/packages/tests/fixtures/video_chapters.mp4 new file mode 100644 index 000000000..46cbaf624 --- /dev/null +++ b/packages/tests/fixtures/video_chapters.mp4 | |||
Binary files differ | |||
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' | |||
30 | import './video-captions.js' | 30 | import './video-captions.js' |
31 | import './video-channel-syncs.js' | 31 | import './video-channel-syncs.js' |
32 | import './video-channels.js' | 32 | import './video-channels.js' |
33 | import './video-chapters.js' | ||
33 | import './video-comments.js' | 34 | import './video-comments.js' |
34 | import './video-files.js' | 35 | import './video-files.js' |
35 | import './video-imports.js' | 36 | import './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 | |||
3 | import { HttpStatusCode, Video, VideoCreateResult, VideoPrivacy } from '@peertube/peertube-models' | ||
4 | import { | ||
5 | PeerTubeServer, | ||
6 | cleanupTests, | ||
7 | createSingleServer, | ||
8 | setAccessTokensToServers, | ||
9 | setDefaultVideoChannel | ||
10 | } from '@peertube/peertube-server-commands' | ||
11 | |||
12 | describe('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' | |||
4 | import './video-captions.js' | 4 | import './video-captions.js' |
5 | import './video-change-ownership.js' | 5 | import './video-change-ownership.js' |
6 | import './video-channels.js' | 6 | import './video-channels.js' |
7 | import './video-chapters.js' | ||
7 | import './channel-import-videos.js' | 8 | import './channel-import-videos.js' |
8 | import './video-channel-syncs.js' | 9 | import './video-channel-syncs.js' |
9 | import './video-comments.js' | 10 | import './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 | |||
3 | import { VideoChapter, VideoCreateResult, VideoPrivacy } from '@peertube/peertube-models' | ||
4 | import { areHttpImportTestsDisabled } from '@peertube/peertube-node-utils' | ||
5 | import { | ||
6 | cleanupTests, | ||
7 | createMultipleServers, | ||
8 | doubleFollow, PeerTubeServer, setAccessTokensToServers, | ||
9 | setDefaultVideoChannel, | ||
10 | waitJobs | ||
11 | } from '@peertube/peertube-server-commands' | ||
12 | import { FIXTURE_URLS } from '@tests/shared/tests.js' | ||
13 | import { expect } from 'chai' | ||
14 | |||
15 | describe('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 | }) | ||
diff --git a/packages/tests/src/server-helpers/core-utils.ts b/packages/tests/src/server-helpers/core-utils.ts index d61cae855..0df238e88 100644 --- a/packages/tests/src/server-helpers/core-utils.ts +++ b/packages/tests/src/server-helpers/core-utils.ts | |||
@@ -3,7 +3,7 @@ | |||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import snakeCase from 'lodash-es/snakeCase.js' | 4 | import snakeCase from 'lodash-es/snakeCase.js' |
5 | import validator from 'validator' | 5 | import validator from 'validator' |
6 | import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate } from '@peertube/peertube-core-utils' | 6 | import { getAverageTheoreticalBitrate, getMaxTheoreticalBitrate, parseChapters } from '@peertube/peertube-core-utils' |
7 | import { VideoResolution } from '@peertube/peertube-models' | 7 | import { VideoResolution } from '@peertube/peertube-models' |
8 | import { objectConverter, parseBytes, parseDurationToMs, parseSemVersion } from '@peertube/peertube-server/server/helpers/core-utils.js' | 8 | import { objectConverter, parseBytes, parseDurationToMs, parseSemVersion } from '@peertube/peertube-server/server/helpers/core-utils.js' |
9 | 9 | ||
@@ -199,3 +199,28 @@ describe('Parse semantic version string', function () { | |||
199 | expect(actual.patch).to.equal(0) | 199 | expect(actual.patch).to.equal(0) |
200 | }) | 200 | }) |
201 | }) | 201 | }) |
202 | |||
203 | describe('Extract chapters', function () { | ||
204 | |||
205 | it('Should not extract chapters', function () { | ||
206 | expect(parseChapters('my super description\nno?')).to.deep.equal([]) | ||
207 | expect(parseChapters('m00:00 super description\nno?')).to.deep.equal([]) | ||
208 | expect(parseChapters('00:00super description\nno?')).to.deep.equal([]) | ||
209 | }) | ||
210 | |||
211 | it('Should extract chapters', function () { | ||
212 | expect(parseChapters('00:00 coucou')).to.deep.equal([ { timecode: 0, title: 'coucou' } ]) | ||
213 | expect(parseChapters('my super description\n\n00:01:30 chapter 1\n00:01:35 chapter 2')).to.deep.equal([ | ||
214 | { timecode: 90, title: 'chapter 1' }, | ||
215 | { timecode: 95, title: 'chapter 2' } | ||
216 | ]) | ||
217 | expect(parseChapters('hi\n\n00:01:30 chapter 1\n00:01:35 chapter 2\nhi')).to.deep.equal([ | ||
218 | { timecode: 90, title: 'chapter 1' }, | ||
219 | { timecode: 95, title: 'chapter 2' } | ||
220 | ]) | ||
221 | expect(parseChapters('hi\n\n00:01:30 chapter 1\n00:01:35 chapter 2\nhi\n00:01:40 chapter 3')).to.deep.equal([ | ||
222 | { timecode: 90, title: 'chapter 1' }, | ||
223 | { timecode: 95, title: 'chapter 2' } | ||
224 | ]) | ||
225 | }) | ||
226 | }) | ||
diff --git a/packages/tests/src/shared/tests.ts b/packages/tests/src/shared/tests.ts index d2cb040fb..554ed0e1f 100644 --- a/packages/tests/src/shared/tests.ts +++ b/packages/tests/src/shared/tests.ts | |||
@@ -3,6 +3,7 @@ const FIXTURE_URLS = { | |||
3 | peertube_short: 'https://peertube2.cpy.re/w/3fbif9S3WmtTP8gGsC5HBd', | 3 | peertube_short: 'https://peertube2.cpy.re/w/3fbif9S3WmtTP8gGsC5HBd', |
4 | 4 | ||
5 | youtube: 'https://www.youtube.com/watch?v=msX3jv1XdvM', | 5 | youtube: 'https://www.youtube.com/watch?v=msX3jv1XdvM', |
6 | youtubeChapters: 'https://www.youtube.com/watch?v=TL9P-Er7ils', | ||
6 | 7 | ||
7 | /** | 8 | /** |
8 | * The video is used to check format-selection correctness wrt. HDR, | 9 | * The video is used to check format-selection correctness wrt. HDR, |
@@ -26,6 +27,8 @@ const FIXTURE_URLS = { | |||
26 | goodVideo: 'https://download.cpy.re/peertube/good_video.mp4', | 27 | goodVideo: 'https://download.cpy.re/peertube/good_video.mp4', |
27 | goodVideo720: 'https://download.cpy.re/peertube/good_video_720.mp4', | 28 | goodVideo720: 'https://download.cpy.re/peertube/good_video_720.mp4', |
28 | 29 | ||
30 | chatersVideo: 'https://download.cpy.re/peertube/video_chapters.mp4', | ||
31 | |||
29 | file4K: 'https://download.cpy.re/peertube/4k_file.txt' | 32 | file4K: 'https://download.cpy.re/peertube/4k_file.txt' |
30 | } | 33 | } |
31 | 34 | ||