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