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