]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/utils/videos/videos.ts
Implement support field in video and video channel
[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 host: string
372 account: string
373 isLocal: boolean,
374 tags: string[],
375 privacy: number,
376 likes?: number,
377 dislikes?: number,
378 duration: number,
379 channel: {
380 name: string,
381 description
382 isLocal: boolean
383 }
384 fixture: string,
385 files: {
386 resolution: number
387 size: number
388 }[],
389 thumbnailfile?: string
390 previewfile?: string
391 }
392 ) {
393 if (!attributes.likes) attributes.likes = 0
394 if (!attributes.dislikes) attributes.dislikes = 0
395
396 expect(video.name).to.equal(attributes.name)
397 expect(video.category).to.equal(attributes.category)
398 expect(video.categoryLabel).to.equal(VIDEO_CATEGORIES[attributes.category] || 'Misc')
399 expect(video.licence).to.equal(attributes.licence)
400 expect(video.licenceLabel).to.equal(VIDEO_LICENCES[attributes.licence] || 'Unknown')
401 expect(video.language).to.equal(attributes.language)
402 expect(video.languageLabel).to.equal(VIDEO_LANGUAGES[attributes.language] || 'Unknown')
403 expect(video.nsfw).to.equal(attributes.nsfw)
404 expect(video.description).to.equal(attributes.description)
405 expect(video.serverHost).to.equal(attributes.host)
406 expect(video.accountName).to.equal(attributes.account)
407 expect(video.likes).to.equal(attributes.likes)
408 expect(video.dislikes).to.equal(attributes.dislikes)
409 expect(video.isLocal).to.equal(attributes.isLocal)
410 expect(video.duration).to.equal(attributes.duration)
411 expect(dateIsValid(video.createdAt)).to.be.true
412 expect(dateIsValid(video.updatedAt)).to.be.true
413
414 const res = await getVideo(url, video.uuid)
415 const videoDetails = res.body
416
417 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
418 expect(videoDetails.tags).to.deep.equal(attributes.tags)
419 expect(videoDetails.privacy).to.deep.equal(attributes.privacy)
420 expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
421 expect(videoDetails.account.name).to.equal(attributes.account)
422 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
423
424 expect(videoDetails.channel.displayName).to.equal(attributes.channel.name)
425 expect(videoDetails.channel.name).to.have.lengthOf(36)
426 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
427 expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
428 expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
429
430 for (const attributeFile of attributes.files) {
431 const file = videoDetails.files.find(f => f.resolution === attributeFile.resolution)
432 expect(file).not.to.be.undefined
433
434 let extension = extname(attributes.fixture)
435 // Transcoding enabled on server 2, extension will always be .mp4
436 if (attributes.host === 'localhost:9002') extension = '.mp4'
437
438 const magnetUri = file.magnetUri
439 expect(file.magnetUri).to.have.lengthOf.above(2)
440 expect(file.torrentUrl).to.equal(`http://${attributes.host}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
441 expect(file.fileUrl).to.equal(`http://${attributes.host}/static/webseed/${videoDetails.uuid}-${file.resolution}${extension}`)
442 expect(file.resolution).to.equal(attributeFile.resolution)
443 expect(file.resolutionLabel).to.equal(attributeFile.resolution + 'p')
444
445 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
446 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
447 expect(file.size).to.be.above(minSize).and.below(maxSize)
448
449 {
450 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
451 }
452
453 if (attributes.previewfile) {
454 await testImage(url, attributes.previewfile, videoDetails.previewPath)
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 }