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