]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/transcoding/video-editor.ts
Create another test suite for transcoding jobs
[github/Chocobozzz/PeerTube.git] / server / tests / api / transcoding / video-editor.ts
1 import { expect } from 'chai'
2 import { expectStartWith, getAllFiles } from '@server/tests/shared'
3 import { areObjectStorageTestsDisabled } from '@shared/core-utils'
4 import { VideoEditorTask } from '@shared/models'
5 import {
6 cleanupTests,
7 createMultipleServers,
8 doubleFollow,
9 ObjectStorageCommand,
10 PeerTubeServer,
11 setAccessTokensToServers,
12 setDefaultVideoChannel,
13 VideoEditorCommand,
14 waitJobs
15 } from '@shared/server-commands'
16
17 describe('Test video editor', function () {
18 let servers: PeerTubeServer[] = []
19 let videoUUID: string
20
21 async function checkDuration (server: PeerTubeServer, duration: number) {
22 const video = await server.videos.get({ id: videoUUID })
23
24 expect(video.duration).to.be.approximately(duration, 1)
25
26 for (const file of video.files) {
27 const metadata = await server.videos.getFileMetadata({ url: file.metadataUrl })
28
29 for (const stream of metadata.streams) {
30 expect(Math.round(stream.duration)).to.be.approximately(duration, 1)
31 }
32 }
33 }
34
35 async function renewVideo (fixture = 'video_short.webm') {
36 const video = await servers[0].videos.quickUpload({ name: 'video', fixture })
37 videoUUID = video.uuid
38
39 await waitJobs(servers)
40 }
41
42 async function createTasks (tasks: VideoEditorTask[]) {
43 await servers[0].videoEditor.createEditionTasks({ videoId: videoUUID, tasks })
44 await waitJobs(servers)
45 }
46
47 before(async function () {
48 this.timeout(120_000)
49
50 servers = await createMultipleServers(2)
51
52 await setAccessTokensToServers(servers)
53 await setDefaultVideoChannel(servers)
54
55 await doubleFollow(servers[0], servers[1])
56
57 await servers[0].config.enableMinimumTranscoding()
58
59 await servers[0].config.updateExistingSubConfig({
60 newConfig: {
61 videoEditor: {
62 enabled: true
63 }
64 }
65 })
66 })
67
68 describe('Cutting', function () {
69
70 it('Should cut the beginning of the video', async function () {
71 this.timeout(120_000)
72
73 await renewVideo()
74 await waitJobs(servers)
75
76 const beforeTasks = new Date()
77
78 await createTasks([
79 {
80 name: 'cut',
81 options: {
82 start: 2
83 }
84 }
85 ])
86
87 for (const server of servers) {
88 await checkDuration(server, 3)
89
90 const video = await server.videos.get({ id: videoUUID })
91 expect(new Date(video.publishedAt)).to.be.below(beforeTasks)
92 }
93 })
94
95 it('Should cut the end of the video', async function () {
96 this.timeout(120_000)
97 await renewVideo()
98
99 await createTasks([
100 {
101 name: 'cut',
102 options: {
103 end: 2
104 }
105 }
106 ])
107
108 for (const server of servers) {
109 await checkDuration(server, 2)
110 }
111 })
112
113 it('Should cut start/end of the video', async function () {
114 this.timeout(120_000)
115 await renewVideo('video_short1.webm') // 10 seconds video duration
116
117 await createTasks([
118 {
119 name: 'cut',
120 options: {
121 start: 2,
122 end: 6
123 }
124 }
125 ])
126
127 for (const server of servers) {
128 await checkDuration(server, 4)
129 }
130 })
131 })
132
133 describe('Intro/Outro', function () {
134
135 it('Should add an intro', async function () {
136 this.timeout(120_000)
137 await renewVideo()
138
139 await createTasks([
140 {
141 name: 'add-intro',
142 options: {
143 file: 'video_short.webm'
144 }
145 }
146 ])
147
148 for (const server of servers) {
149 await checkDuration(server, 10)
150 }
151 })
152
153 it('Should add an outro', async function () {
154 this.timeout(120_000)
155 await renewVideo()
156
157 await createTasks([
158 {
159 name: 'add-outro',
160 options: {
161 file: 'video_very_short_240p.mp4'
162 }
163 }
164 ])
165
166 for (const server of servers) {
167 await checkDuration(server, 7)
168 }
169 })
170
171 it('Should add an intro/outro', async function () {
172 this.timeout(120_000)
173 await renewVideo()
174
175 await createTasks([
176 {
177 name: 'add-intro',
178 options: {
179 file: 'video_very_short_240p.mp4'
180 }
181 },
182 {
183 name: 'add-outro',
184 options: {
185 // Different frame rate
186 file: 'video_short2.webm'
187 }
188 }
189 ])
190
191 for (const server of servers) {
192 await checkDuration(server, 12)
193 }
194 })
195
196 it('Should add an intro to a video without audio', async function () {
197 this.timeout(120_000)
198 await renewVideo('video_short_no_audio.mp4')
199
200 await createTasks([
201 {
202 name: 'add-intro',
203 options: {
204 file: 'video_very_short_240p.mp4'
205 }
206 }
207 ])
208
209 for (const server of servers) {
210 await checkDuration(server, 7)
211 }
212 })
213
214 it('Should add an outro without audio to a video with audio', async function () {
215 this.timeout(120_000)
216 await renewVideo()
217
218 await createTasks([
219 {
220 name: 'add-outro',
221 options: {
222 file: 'video_short_no_audio.mp4'
223 }
224 }
225 ])
226
227 for (const server of servers) {
228 await checkDuration(server, 10)
229 }
230 })
231
232 it('Should add an outro without audio to a video with audio', async function () {
233 this.timeout(120_000)
234 await renewVideo('video_short_no_audio.mp4')
235
236 await createTasks([
237 {
238 name: 'add-outro',
239 options: {
240 file: 'video_short_no_audio.mp4'
241 }
242 }
243 ])
244
245 for (const server of servers) {
246 await checkDuration(server, 10)
247 }
248 })
249 })
250
251 describe('Watermark', function () {
252
253 it('Should add a watermark to the video', async function () {
254 this.timeout(120_000)
255 await renewVideo()
256
257 const video = await servers[0].videos.get({ id: videoUUID })
258 const oldFileUrls = getAllFiles(video).map(f => f.fileUrl)
259
260 await createTasks([
261 {
262 name: 'add-watermark',
263 options: {
264 file: 'thumbnail.png'
265 }
266 }
267 ])
268
269 for (const server of servers) {
270 const video = await server.videos.get({ id: videoUUID })
271 const fileUrls = getAllFiles(video).map(f => f.fileUrl)
272
273 for (const oldUrl of oldFileUrls) {
274 expect(fileUrls).to.not.include(oldUrl)
275 }
276 }
277 })
278 })
279
280 describe('Complex tasks', function () {
281 it('Should run a complex task', async function () {
282 this.timeout(240_000)
283 await renewVideo()
284
285 await createTasks(VideoEditorCommand.getComplexTask())
286
287 for (const server of servers) {
288 await checkDuration(server, 9)
289 }
290 })
291 })
292
293 describe('HLS only video edition', function () {
294
295 before(async function () {
296 // Disable webtorrent
297 await servers[0].config.updateExistingSubConfig({
298 newConfig: {
299 transcoding: {
300 webtorrent: {
301 enabled: false
302 }
303 }
304 }
305 })
306 })
307
308 it('Should run a complex task on HLS only video', async function () {
309 this.timeout(240_000)
310 await renewVideo()
311
312 await createTasks(VideoEditorCommand.getComplexTask())
313
314 for (const server of servers) {
315 const video = await server.videos.get({ id: videoUUID })
316 expect(video.files).to.have.lengthOf(0)
317
318 await checkDuration(server, 9)
319 }
320 })
321 })
322
323 describe('Object storage video edition', function () {
324 if (areObjectStorageTestsDisabled()) return
325
326 before(async function () {
327 await ObjectStorageCommand.prepareDefaultBuckets()
328
329 await servers[0].kill()
330 await servers[0].run(ObjectStorageCommand.getDefaultConfig())
331
332 await servers[0].config.enableMinimumTranscoding()
333 })
334
335 it('Should run a complex task on a video in object storage', async function () {
336 this.timeout(240_000)
337 await renewVideo()
338
339 const video = await servers[0].videos.get({ id: videoUUID })
340 const oldFileUrls = getAllFiles(video).map(f => f.fileUrl)
341
342 await createTasks(VideoEditorCommand.getComplexTask())
343
344 for (const server of servers) {
345 const video = await server.videos.get({ id: videoUUID })
346 const files = getAllFiles(video)
347
348 for (const f of files) {
349 expect(oldFileUrls).to.not.include(f.fileUrl)
350 }
351
352 for (const webtorrentFile of video.files) {
353 expectStartWith(webtorrentFile.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
354 }
355
356 for (const hlsFile of video.streamingPlaylists[0].files) {
357 expectStartWith(hlsFile.fileUrl, ObjectStorageCommand.getPlaylistBaseUrl())
358 }
359
360 await checkDuration(server, 9)
361 }
362 })
363 })
364
365 after(async function () {
366 await cleanupTests(servers)
367 })
368 })