]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/utils/videos/videos.ts
Delete correctly redundancy files
[github/Chocobozzz/PeerTube.git] / server / tests / utils / videos / videos.ts
CommitLineData
a20399c9
C
1/* tslint:disable:no-unused-expression */
2
3import { expect } from 'chai'
62689b94 4import { existsSync, readdir, readFile } from 'fs-extra'
fdbda9e3 5import * as parseTorrent from 'parse-torrent'
1d791a26 6import { extname, join } from 'path'
c5d31dba 7import * as request from 'supertest'
ac81d1a0
C
8import {
9 buildAbsoluteFixturePath,
62689b94
C
10 getMyUserInformation,
11 immutableAssign,
ac81d1a0
C
12 makeGetRequest,
13 makePutBodyRequest,
14 makeUploadRequest,
15 root,
16 ServerInfo,
17 testImage
18} from '../'
0f320037 19import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
a20399c9
C
20import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
21import { dateIsValid, webtorrentAdd } from '../index'
0e1dc3e7
C
22
23type VideoAttributes = {
24 name?: string
25 category?: number
26 licence?: number
9d3ef9fe 27 language?: string
0e1dc3e7 28 nsfw?: boolean
47564bbe 29 commentsEnabled?: boolean
2186386c 30 waitTranscoding?: boolean
0e1dc3e7
C
31 description?: string
32 tags?: string[]
5f04dd2f 33 channelId?: number
11474c3c 34 privacy?: VideoPrivacy
0e1dc3e7 35 fixture?: string
ac81d1a0
C
36 thumbnailfile?: string
37 previewfile?: string
2baea0c7
C
38 scheduleUpdate?: {
39 updateAt: string
40 privacy?: VideoPrivacy
41 }
0e1dc3e7
C
42}
43
44function getVideoCategories (url: string) {
45 const path = '/api/v1/videos/categories'
46
eec63bbc
C
47 return makeGetRequest({
48 url,
59651eee
C
49 path,
50 statusCodeExpected: 200
eec63bbc 51 })
0e1dc3e7
C
52}
53
54function getVideoLicences (url: string) {
55 const path = '/api/v1/videos/licences'
56
eec63bbc
C
57 return makeGetRequest({
58 url,
59651eee
C
59 path,
60 statusCodeExpected: 200
eec63bbc 61 })
0e1dc3e7
C
62}
63
64function getVideoLanguages (url: string) {
65 const path = '/api/v1/videos/languages'
66
eec63bbc
C
67 return makeGetRequest({
68 url,
59651eee
C
69 path,
70 statusCodeExpected: 200
eec63bbc 71 })
0e1dc3e7
C
72}
73
11474c3c
C
74function getVideoPrivacies (url: string) {
75 const path = '/api/v1/videos/privacies'
76
eec63bbc
C
77 return makeGetRequest({
78 url,
59651eee
C
79 path,
80 statusCodeExpected: 200
eec63bbc 81 })
11474c3c
C
82}
83
11474c3c 84function getVideo (url: string, id: number | string, expectedStatus = 200) {
0e1dc3e7
C
85 const path = '/api/v1/videos/' + id
86
87 return request(url)
88 .get(path)
89 .set('Accept', 'application/json')
11474c3c
C
90 .expect(expectedStatus)
91}
92
490b595a 93function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
1f3e9fec
C
94 const path = '/api/v1/videos/' + id + '/views'
95
490b595a 96 const req = request(url)
1f3e9fec
C
97 .post(path)
98 .set('Accept', 'application/json')
490b595a
C
99
100 if (xForwardedFor) {
101 req.set('X-Forwarded-For', xForwardedFor)
102 }
103
104 return req.expect(expectedStatus)
1f3e9fec
C
105}
106
11474c3c
C
107function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
108 const path = '/api/v1/videos/' + id
109
110 return request(url)
111 .get(path)
112 .set('Authorization', 'Bearer ' + token)
113 .set('Accept', 'application/json')
114 .expect(expectedStatus)
0e1dc3e7
C
115}
116
9567011b
C
117function getVideoDescription (url: string, descriptionPath: string) {
118 return request(url)
119 .get(descriptionPath)
120 .set('Accept', 'application/json')
121 .expect(200)
122 .expect('Content-Type', /json/)
123}
124
0e1dc3e7
C
125function getVideosList (url: string) {
126 const path = '/api/v1/videos'
127
128 return request(url)
129 .get(path)
130 .query({ sort: 'name' })
131 .set('Accept', 'application/json')
132 .expect(200)
133 .expect('Content-Type', /json/)
134}
135
d525fc39 136function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
0883b324
C
137 const path = '/api/v1/videos'
138
139 return request(url)
140 .get(path)
141 .set('Authorization', 'Bearer ' + token)
d525fc39 142 .query(immutableAssign(query, { sort: 'name' }))
0883b324
C
143 .set('Accept', 'application/json')
144 .expect(200)
145 .expect('Content-Type', /json/)
146}
147
066e94c5
C
148function getLocalVideos (url: string) {
149 const path = '/api/v1/videos'
150
151 return request(url)
152 .get(path)
153 .query({ sort: 'name', filter: 'local' })
154 .set('Accept', 'application/json')
155 .expect(200)
156 .expect('Content-Type', /json/)
157}
158
11474c3c
C
159function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
160 const path = '/api/v1/users/me/videos'
161
162 const req = request(url)
163 .get(path)
164 .query({ start: start })
165 .query({ count: count })
166
167 if (sort) req.query({ sort })
168
169 return req.set('Accept', 'application/json')
170 .set('Authorization', 'Bearer ' + accessToken)
171 .expect(200)
172 .expect('Content-Type', /json/)
173}
174
d525fc39
C
175function getAccountVideos (
176 url: string,
177 accessToken: string,
178 accountName: string,
179 start: number,
180 count: number,
181 sort?: string,
182 query: { nsfw?: boolean } = {}
183) {
ad9e39fb 184 const path = '/api/v1/accounts/' + accountName + '/videos'
6b738c7a
C
185
186 return makeGetRequest({
187 url,
188 path,
d525fc39 189 query: immutableAssign(query, {
6b738c7a
C
190 start,
191 count,
192 sort
d525fc39 193 }),
6b738c7a
C
194 token: accessToken,
195 statusCodeExpected: 200
196 })
197}
198
199function getVideoChannelVideos (
200 url: string,
201 accessToken: string,
8a19bee1 202 videoChannelName: string,
6b738c7a
C
203 start: number,
204 count: number,
d525fc39
C
205 sort?: string,
206 query: { nsfw?: boolean } = {}
6b738c7a 207) {
8a19bee1 208 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
6b738c7a
C
209
210 return makeGetRequest({
211 url,
212 path,
d525fc39 213 query: immutableAssign(query, {
6b738c7a
C
214 start,
215 count,
216 sort
d525fc39 217 }),
6b738c7a
C
218 token: accessToken,
219 statusCodeExpected: 200
220 })
221}
222
0e1dc3e7
C
223function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
224 const path = '/api/v1/videos'
225
226 const req = request(url)
227 .get(path)
228 .query({ start: start })
229 .query({ count: count })
230
231 if (sort) req.query({ sort })
232
233 return req.set('Accept', 'application/json')
234 .expect(200)
235 .expect('Content-Type', /json/)
236}
237
238function getVideosListSort (url: string, sort: string) {
239 const path = '/api/v1/videos'
240
241 return request(url)
242 .get(path)
243 .query({ sort: sort })
244 .set('Accept', 'application/json')
245 .expect(200)
246 .expect('Content-Type', /json/)
247}
248
d525fc39 249function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) {
0e1dc3e7
C
250 const path = '/api/v1/videos'
251
252 return request(url)
57c36b27 253 .get(path)
d525fc39 254 .query(query)
f3aaa9a9 255 .set('Accept', 'application/json')
d525fc39 256 .expect(200)
f3aaa9a9 257 .expect('Content-Type', /json/)
0e1dc3e7
C
258}
259
d525fc39 260function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
0883b324 261 const path = '/api/v1/videos'
0e1dc3e7
C
262
263 return request(url)
d525fc39 264 .delete(path + '/' + id)
0e1dc3e7 265 .set('Accept', 'application/json')
d525fc39
C
266 .set('Authorization', 'Bearer ' + token)
267 .expect(expectedStatus)
0e1dc3e7
C
268}
269
25378bc8
C
270async function checkVideoFilesWereRemoved (
271 videoUUID: string,
272 serverNumber: number,
273 directories = [ 'videos', 'thumbnails', 'torrents', 'previews', 'captions' ]
274) {
f05a1c30
C
275 const testDirectory = 'test' + serverNumber
276
25378bc8 277 for (const directory of directories) {
f05a1c30
C
278 const directoryPath = join(root(), testDirectory, directory)
279
280 const directoryExists = existsSync(directoryPath)
281 expect(directoryExists).to.be.true
282
62689b94 283 const files = await readdir(directoryPath)
f05a1c30
C
284 for (const file of files) {
285 expect(file).to.not.contain(videoUUID)
286 }
287 }
288}
289
e11f68a3 290async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
e95561cd 291 const path = '/api/v1/videos/upload'
5f04dd2f
C
292 let defaultChannelId = '1'
293
294 try {
295 const res = await getMyUserInformation(url, accessToken)
296 defaultChannelId = res.body.videoChannels[0].id
297 } catch (e) { /* empty */ }
0e1dc3e7 298
ac81d1a0
C
299 // Override default attributes
300 const attributes = Object.assign({
0e1dc3e7
C
301 name: 'my super video',
302 category: 5,
303 licence: 4,
9d3ef9fe 304 language: 'zh',
5f04dd2f 305 channelId: defaultChannelId,
0e1dc3e7 306 nsfw: true,
2186386c 307 waitTranscoding: false,
0e1dc3e7 308 description: 'my super description',
2422c46b 309 support: 'my super support text',
0e1dc3e7 310 tags: [ 'tag' ],
11474c3c 311 privacy: VideoPrivacy.PUBLIC,
47564bbe 312 commentsEnabled: true,
0e1dc3e7 313 fixture: 'video_short.webm'
ac81d1a0 314 }, videoAttributesArg)
0e1dc3e7
C
315
316 const req = request(url)
317 .post(path)
318 .set('Accept', 'application/json')
319 .set('Authorization', 'Bearer ' + accessToken)
320 .field('name', attributes.name)
0e1dc3e7 321 .field('nsfw', JSON.stringify(attributes.nsfw))
47564bbe 322 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
2186386c 323 .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
11474c3c 324 .field('privacy', attributes.privacy.toString())
5f04dd2f 325 .field('channelId', attributes.channelId)
0e1dc3e7 326
a87d467a
C
327 if (attributes.description !== undefined) {
328 req.field('description', attributes.description)
329 }
8df87ce7
C
330 if (attributes.language !== undefined) {
331 req.field('language', attributes.language.toString())
332 }
a7fea183
C
333 if (attributes.category !== undefined) {
334 req.field('category', attributes.category.toString())
335 }
336 if (attributes.licence !== undefined) {
337 req.field('licence', attributes.licence.toString())
338 }
8df87ce7 339
2422c46b
C
340 for (let i = 0; i < attributes.tags.length; i++) {
341 req.field('tags[' + i + ']', attributes.tags[i])
342 }
343
ac81d1a0
C
344 if (attributes.thumbnailfile !== undefined) {
345 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
346 }
347 if (attributes.previewfile !== undefined) {
348 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
0e1dc3e7
C
349 }
350
2baea0c7
C
351 if (attributes.scheduleUpdate) {
352 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
353
354 if (attributes.scheduleUpdate.privacy) {
355 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
356 }
357 }
358
ac81d1a0 359 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
0e1dc3e7
C
360 .expect(specialStatus)
361}
362
ac81d1a0 363function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
0e1dc3e7
C
364 const path = '/api/v1/videos/' + id
365 const body = {}
366
367 if (attributes.name) body['name'] = attributes.name
368 if (attributes.category) body['category'] = attributes.category
369 if (attributes.licence) body['licence'] = attributes.licence
370 if (attributes.language) body['language'] = attributes.language
47564bbe
C
371 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
372 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
0e1dc3e7
C
373 if (attributes.description) body['description'] = attributes.description
374 if (attributes.tags) body['tags'] = attributes.tags
11474c3c 375 if (attributes.privacy) body['privacy'] = attributes.privacy
0f320037 376 if (attributes.channelId) body['channelId'] = attributes.channelId
2baea0c7 377 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
0e1dc3e7 378
ac81d1a0
C
379 // Upload request
380 if (attributes.thumbnailfile || attributes.previewfile) {
381 const attaches: any = {}
382 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
383 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
384
385 return makeUploadRequest({
386 url,
387 method: 'PUT',
388 path,
389 token: accessToken,
390 fields: body,
391 attaches,
392 statusCodeExpected
393 })
394 }
395
396 return makePutBodyRequest({
397 url,
398 path,
399 fields: body,
400 token: accessToken,
401 statusCodeExpected
402 })
0e1dc3e7
C
403}
404
405function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
406 const path = '/api/v1/videos/' + id + '/rate'
407
408 return request(url)
409 .put(path)
410 .set('Accept', 'application/json')
411 .set('Authorization', 'Bearer ' + accessToken)
412 .send({ rating })
413 .expect(specialStatus)
414}
415
14d3270f 416function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
fdbda9e3 417 return new Promise<any>((res, rej) => {
14d3270f 418 const torrentName = videoUUID + '-' + resolution + '.torrent'
331128ed 419 const torrentPath = join(__dirname, '..', '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
fdbda9e3
C
420 readFile(torrentPath, (err, data) => {
421 if (err) return rej(err)
422
423 return res(parseTorrent(data))
424 })
425 })
426}
427
a20399c9
C
428async function completeVideoCheck (
429 url: string,
430 video: any,
431 attributes: {
432 name: string
433 category: number
434 licence: number
9d3ef9fe 435 language: string
a20399c9 436 nsfw: boolean
47564bbe 437 commentsEnabled: boolean
a20399c9 438 description: string
53a61317 439 publishedAt?: string
2422c46b 440 support: string
b64c950a
C
441 account: {
442 name: string
443 host: string
444 }
f6eebcb3
C
445 isLocal: boolean
446 tags: string[]
447 privacy: number
448 likes?: number
449 dislikes?: number
450 duration: number
a20399c9 451 channel: {
f6eebcb3
C
452 displayName: string
453 name: string
b1f5b93e 454 description
a20399c9
C
455 isLocal: boolean
456 }
f6eebcb3 457 fixture: string
a20399c9
C
458 files: {
459 resolution: number
460 size: number
ac81d1a0
C
461 }[],
462 thumbnailfile?: string
463 previewfile?: string
a20399c9
C
464 }
465) {
b1f5b93e
C
466 if (!attributes.likes) attributes.likes = 0
467 if (!attributes.dislikes) attributes.dislikes = 0
468
a20399c9 469 expect(video.name).to.equal(attributes.name)
09700934 470 expect(video.category.id).to.equal(attributes.category)
9d3ef9fe 471 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
09700934 472 expect(video.licence.id).to.equal(attributes.licence)
9d3ef9fe 473 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
09700934 474 expect(video.language.id).to.equal(attributes.language)
9d3ef9fe 475 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
2243730c
C
476 expect(video.privacy.id).to.deep.equal(attributes.privacy)
477 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
a20399c9
C
478 expect(video.nsfw).to.equal(attributes.nsfw)
479 expect(video.description).to.equal(attributes.description)
03e12d7c
C
480 expect(video.account.id).to.be.a('number')
481 expect(video.account.uuid).to.be.a('string')
b64c950a
C
482 expect(video.account.host).to.equal(attributes.account.host)
483 expect(video.account.name).to.equal(attributes.account.name)
f6eebcb3
C
484 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
485 expect(video.channel.name).to.equal(attributes.channel.name)
b1f5b93e
C
486 expect(video.likes).to.equal(attributes.likes)
487 expect(video.dislikes).to.equal(attributes.dislikes)
a20399c9 488 expect(video.isLocal).to.equal(attributes.isLocal)
b1f5b93e 489 expect(video.duration).to.equal(attributes.duration)
a20399c9 490 expect(dateIsValid(video.createdAt)).to.be.true
c49db162 491 expect(dateIsValid(video.publishedAt)).to.be.true
a20399c9
C
492 expect(dateIsValid(video.updatedAt)).to.be.true
493
53a61317 494 if (attributes.publishedAt) {
53a61317
C
495 expect(video.publishedAt).to.equal(attributes.publishedAt)
496 }
497
66b16caf 498 const res = await getVideo(url, video.uuid)
0f320037 499 const videoDetails: VideoDetails = res.body
a20399c9
C
500
501 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
502 expect(videoDetails.tags).to.deep.equal(attributes.tags)
19a3b914
C
503 expect(videoDetails.account.name).to.equal(attributes.account.name)
504 expect(videoDetails.account.host).to.equal(attributes.account.host)
f6eebcb3
C
505 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
506 expect(video.channel.name).to.equal(attributes.channel.name)
0f320037 507 expect(videoDetails.channel.host).to.equal(attributes.account.host)
a20399c9 508 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
0f320037
C
509 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
510 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
511 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
a20399c9
C
512
513 for (const attributeFile of attributes.files) {
5d00a3d7 514 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
a20399c9
C
515 expect(file).not.to.be.undefined
516
b1f5b93e
C
517 let extension = extname(attributes.fixture)
518 // Transcoding enabled on server 2, extension will always be .mp4
b64c950a 519 if (attributes.account.host === 'localhost:9002') extension = '.mp4'
b1f5b93e 520
a20399c9
C
521 const magnetUri = file.magnetUri
522 expect(file.magnetUri).to.have.lengthOf.above(2)
5d00a3d7
C
523 expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
524 expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
09700934
C
525 expect(file.resolution.id).to.equal(attributeFile.resolution)
526 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
b1f5b93e
C
527
528 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
529 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
4176e227 530 expect(file.size,
7160878c 531 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')')
4176e227 532 .to.be.above(minSize).and.below(maxSize)
a20399c9 533
ac81d1a0 534 {
7b0956ec 535 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
ac81d1a0
C
536 }
537
538 if (attributes.previewfile) {
7b0956ec 539 await testImage(url, attributes.previewfile, videoDetails.previewPath)
ac81d1a0 540 }
a20399c9
C
541
542 const torrent = await webtorrentAdd(magnetUri, true)
543 expect(torrent.files).to.be.an('array')
544 expect(torrent.files.length).to.equal(1)
545 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
546 }
547}
548
0e1dc3e7
C
549// ---------------------------------------------------------------------------
550
551export {
9567011b 552 getVideoDescription,
0e1dc3e7
C
553 getVideoCategories,
554 getVideoLicences,
11474c3c 555 getVideoPrivacies,
0e1dc3e7 556 getVideoLanguages,
11474c3c 557 getMyVideos,
6b738c7a
C
558 getAccountVideos,
559 getVideoChannelVideos,
0e1dc3e7 560 getVideo,
11474c3c 561 getVideoWithToken,
0e1dc3e7
C
562 getVideosList,
563 getVideosListPagination,
564 getVideosListSort,
565 removeVideo,
0883b324 566 getVideosListWithToken,
0e1dc3e7 567 uploadVideo,
d525fc39 568 getVideosWithFilters,
0e1dc3e7 569 updateVideo,
fdbda9e3 570 rateVideo,
1f3e9fec 571 viewVideo,
a20399c9 572 parseTorrentVideo,
066e94c5 573 getLocalVideos,
f05a1c30
C
574 completeVideoCheck,
575 checkVideoFilesWereRemoved
0e1dc3e7 576}