]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/utils/videos/videos.ts
Fix video play promise error on non supported browsers
[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 tags: [ 'tag' ],
252 privacy: VideoPrivacy.PUBLIC,
253 commentsEnabled: true,
254 fixture: 'video_short.webm'
255 }, videoAttributesArg)
256
257 const req = request(url)
258 .post(path)
259 .set('Accept', 'application/json')
260 .set('Authorization', 'Bearer ' + accessToken)
261 .field('name', attributes.name)
262 .field('nsfw', JSON.stringify(attributes.nsfw))
263 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
264 .field('privacy', attributes.privacy.toString())
265 .field('channelId', attributes.channelId)
266
267 if (attributes.description !== undefined) {
268 req.field('description', attributes.description)
269 }
270 if (attributes.language !== undefined) {
271 req.field('language', attributes.language.toString())
272 }
273 if (attributes.category !== undefined) {
274 req.field('category', attributes.category.toString())
275 }
276 if (attributes.licence !== undefined) {
277 req.field('licence', attributes.licence.toString())
278 }
279
280 if (attributes.thumbnailfile !== undefined) {
281 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
282 }
283 if (attributes.previewfile !== undefined) {
284 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
285 }
286
287 for (let i = 0; i < attributes.tags.length; i++) {
288 req.field('tags[' + i + ']', attributes.tags[i])
289 }
290
291 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
292 .expect(specialStatus)
293 }
294
295 function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
296 const path = '/api/v1/videos/' + id
297 const body = {}
298
299 if (attributes.name) body['name'] = attributes.name
300 if (attributes.category) body['category'] = attributes.category
301 if (attributes.licence) body['licence'] = attributes.licence
302 if (attributes.language) body['language'] = attributes.language
303 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
304 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
305 if (attributes.description) body['description'] = attributes.description
306 if (attributes.tags) body['tags'] = attributes.tags
307 if (attributes.privacy) body['privacy'] = attributes.privacy
308
309 // Upload request
310 if (attributes.thumbnailfile || attributes.previewfile) {
311 const attaches: any = {}
312 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
313 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
314
315 return makeUploadRequest({
316 url,
317 method: 'PUT',
318 path,
319 token: accessToken,
320 fields: body,
321 attaches,
322 statusCodeExpected
323 })
324 }
325
326 return makePutBodyRequest({
327 url,
328 path,
329 fields: body,
330 token: accessToken,
331 statusCodeExpected
332 })
333 }
334
335 function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
336 const path = '/api/v1/videos/' + id + '/rate'
337
338 return request(url)
339 .put(path)
340 .set('Accept', 'application/json')
341 .set('Authorization', 'Bearer ' + accessToken)
342 .send({ rating })
343 .expect(specialStatus)
344 }
345
346 function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
347 return new Promise<any>((res, rej) => {
348 const torrentName = videoUUID + '-' + resolution + '.torrent'
349 const torrentPath = join(__dirname, '..', '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
350 readFile(torrentPath, (err, data) => {
351 if (err) return rej(err)
352
353 return res(parseTorrent(data))
354 })
355 })
356 }
357
358 async function completeVideoCheck (
359 url: string,
360 video: any,
361 attributes: {
362 name: string
363 category: number
364 licence: number
365 language: number
366 nsfw: boolean
367 commentsEnabled: boolean
368 description: string
369 host: string
370 account: string
371 isLocal: boolean,
372 tags: string[],
373 privacy: number,
374 likes?: number,
375 dislikes?: number,
376 duration: number,
377 channel: {
378 name: string,
379 description
380 isLocal: boolean
381 }
382 fixture: string,
383 files: {
384 resolution: number
385 size: number
386 }[],
387 thumbnailfile?: string
388 previewfile?: string
389 }
390 ) {
391 if (!attributes.likes) attributes.likes = 0
392 if (!attributes.dislikes) attributes.dislikes = 0
393
394 expect(video.name).to.equal(attributes.name)
395 expect(video.category).to.equal(attributes.category)
396 expect(video.categoryLabel).to.equal(VIDEO_CATEGORIES[attributes.category] || 'Misc')
397 expect(video.licence).to.equal(attributes.licence)
398 expect(video.licenceLabel).to.equal(VIDEO_LICENCES[attributes.licence] || 'Unknown')
399 expect(video.language).to.equal(attributes.language)
400 expect(video.languageLabel).to.equal(VIDEO_LANGUAGES[attributes.language] || 'Unknown')
401 expect(video.nsfw).to.equal(attributes.nsfw)
402 expect(video.description).to.equal(attributes.description)
403 expect(video.serverHost).to.equal(attributes.host)
404 expect(video.accountName).to.equal(attributes.account)
405 expect(video.likes).to.equal(attributes.likes)
406 expect(video.dislikes).to.equal(attributes.dislikes)
407 expect(video.isLocal).to.equal(attributes.isLocal)
408 expect(video.duration).to.equal(attributes.duration)
409 expect(dateIsValid(video.createdAt)).to.be.true
410 expect(dateIsValid(video.updatedAt)).to.be.true
411
412 const res = await getVideo(url, video.uuid)
413 const videoDetails = res.body
414
415 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
416 expect(videoDetails.tags).to.deep.equal(attributes.tags)
417 expect(videoDetails.privacy).to.deep.equal(attributes.privacy)
418 expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
419 expect(videoDetails.account.name).to.equal(attributes.account)
420 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
421
422 expect(videoDetails.channel.displayName).to.equal(attributes.channel.name)
423 expect(videoDetails.channel.name).to.have.lengthOf(36)
424 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
425 expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
426 expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
427
428 for (const attributeFile of attributes.files) {
429 const file = videoDetails.files.find(f => f.resolution === attributeFile.resolution)
430 expect(file).not.to.be.undefined
431
432 let extension = extname(attributes.fixture)
433 // Transcoding enabled on server 2, extension will always be .mp4
434 if (attributes.host === 'localhost:9002') extension = '.mp4'
435
436 const magnetUri = file.magnetUri
437 expect(file.magnetUri).to.have.lengthOf.above(2)
438 expect(file.torrentUrl).to.equal(`http://${attributes.host}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
439 expect(file.fileUrl).to.equal(`http://${attributes.host}/static/webseed/${videoDetails.uuid}-${file.resolution}${extension}`)
440 expect(file.resolution).to.equal(attributeFile.resolution)
441 expect(file.resolutionLabel).to.equal(attributeFile.resolution + 'p')
442
443 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
444 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
445 expect(file.size).to.be.above(minSize).and.below(maxSize)
446
447 {
448 const test = await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
449 expect(test).to.equal(true)
450 }
451
452 if (attributes.previewfile) {
453 const test = await testImage(url, attributes.previewfile, videoDetails.previewPath)
454 expect(test).to.equal(true)
455 }
456
457 const torrent = await webtorrentAdd(magnetUri, true)
458 expect(torrent.files).to.be.an('array')
459 expect(torrent.files.length).to.equal(1)
460 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
461 }
462 }
463
464 // ---------------------------------------------------------------------------
465
466 export {
467 getVideoDescription,
468 getVideoCategories,
469 getVideoLicences,
470 getVideoPrivacies,
471 getVideoLanguages,
472 getMyVideos,
473 getVideo,
474 getVideoWithToken,
475 getVideosList,
476 getVideosListPagination,
477 getVideosListSort,
478 removeVideo,
479 searchVideo,
480 searchVideoWithPagination,
481 searchVideoWithSort,
482 uploadVideo,
483 updateVideo,
484 rateVideo,
485 viewVideo,
486 parseTorrentVideo,
487 completeVideoCheck,
488 checkVideoFilesWereRemoved
489 }