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