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