]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/tests/utils/videos/videos.ts
Add downloadingEnabled property to video model
[github/Chocobozzz/PeerTube.git] / server / tests / utils / videos / videos.ts
CommitLineData
a20399c9
C
1/* tslint:disable:no-unused-expression */
2
3import { expect } from 'chai'
62689b94 4import { existsSync, readdir, readFile } from 'fs-extra'
fdbda9e3 5import * as parseTorrent from 'parse-torrent'
1d791a26 6import { extname, join } from 'path'
c5d31dba 7import * as request from 'supertest'
ac81d1a0
C
8import {
9 buildAbsoluteFixturePath,
62689b94
C
10 getMyUserInformation,
11 immutableAssign,
ac81d1a0
C
12 makeGetRequest,
13 makePutBodyRequest,
14 makeUploadRequest,
15 root,
16 ServerInfo,
17 testImage
18} from '../'
0f320037 19import { VideoDetails, VideoPrivacy } from '../../../../shared/models/videos'
a20399c9
C
20import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
21import { dateIsValid, webtorrentAdd } from '../index'
0e1dc3e7
C
22
23type VideoAttributes = {
24 name?: string
25 category?: number
26 licence?: number
9d3ef9fe 27 language?: string
0e1dc3e7 28 nsfw?: boolean
47564bbe 29 commentsEnabled?: boolean
156c50af 30 downloadingEnabled?: boolean
2186386c 31 waitTranscoding?: boolean
0e1dc3e7
C
32 description?: string
33 tags?: string[]
5f04dd2f 34 channelId?: number
11474c3c 35 privacy?: VideoPrivacy
0e1dc3e7 36 fixture?: string
ac81d1a0
C
37 thumbnailfile?: string
38 previewfile?: string
2baea0c7
C
39 scheduleUpdate?: {
40 updateAt: string
41 privacy?: VideoPrivacy
42 }
0e1dc3e7
C
43}
44
45function getVideoCategories (url: string) {
46 const path = '/api/v1/videos/categories'
47
eec63bbc
C
48 return makeGetRequest({
49 url,
59651eee
C
50 path,
51 statusCodeExpected: 200
eec63bbc 52 })
0e1dc3e7
C
53}
54
55function getVideoLicences (url: string) {
56 const path = '/api/v1/videos/licences'
57
eec63bbc
C
58 return makeGetRequest({
59 url,
59651eee
C
60 path,
61 statusCodeExpected: 200
eec63bbc 62 })
0e1dc3e7
C
63}
64
65function getVideoLanguages (url: string) {
66 const path = '/api/v1/videos/languages'
67
eec63bbc
C
68 return makeGetRequest({
69 url,
59651eee
C
70 path,
71 statusCodeExpected: 200
eec63bbc 72 })
0e1dc3e7
C
73}
74
11474c3c
C
75function getVideoPrivacies (url: string) {
76 const path = '/api/v1/videos/privacies'
77
eec63bbc
C
78 return makeGetRequest({
79 url,
59651eee
C
80 path,
81 statusCodeExpected: 200
eec63bbc 82 })
11474c3c
C
83}
84
11474c3c 85function getVideo (url: string, id: number | string, expectedStatus = 200) {
0e1dc3e7
C
86 const path = '/api/v1/videos/' + id
87
88 return request(url)
89 .get(path)
90 .set('Accept', 'application/json')
11474c3c
C
91 .expect(expectedStatus)
92}
93
490b595a 94function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
1f3e9fec
C
95 const path = '/api/v1/videos/' + id + '/views'
96
490b595a 97 const req = request(url)
1f3e9fec
C
98 .post(path)
99 .set('Accept', 'application/json')
490b595a
C
100
101 if (xForwardedFor) {
102 req.set('X-Forwarded-For', xForwardedFor)
103 }
104
105 return req.expect(expectedStatus)
1f3e9fec
C
106}
107
11474c3c
C
108function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
109 const path = '/api/v1/videos/' + id
110
111 return request(url)
112 .get(path)
113 .set('Authorization', 'Bearer ' + token)
114 .set('Accept', 'application/json')
115 .expect(expectedStatus)
0e1dc3e7
C
116}
117
9567011b
C
118function getVideoDescription (url: string, descriptionPath: string) {
119 return request(url)
120 .get(descriptionPath)
121 .set('Accept', 'application/json')
122 .expect(200)
123 .expect('Content-Type', /json/)
124}
125
0e1dc3e7
C
126function getVideosList (url: string) {
127 const path = '/api/v1/videos'
128
129 return request(url)
130 .get(path)
131 .query({ sort: 'name' })
132 .set('Accept', 'application/json')
133 .expect(200)
134 .expect('Content-Type', /json/)
135}
136
d525fc39 137function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
0883b324
C
138 const path = '/api/v1/videos'
139
140 return request(url)
141 .get(path)
142 .set('Authorization', 'Bearer ' + token)
d525fc39 143 .query(immutableAssign(query, { sort: 'name' }))
0883b324
C
144 .set('Accept', 'application/json')
145 .expect(200)
146 .expect('Content-Type', /json/)
147}
148
066e94c5
C
149function getLocalVideos (url: string) {
150 const path = '/api/v1/videos'
151
152 return request(url)
153 .get(path)
154 .query({ sort: 'name', filter: 'local' })
155 .set('Accept', 'application/json')
156 .expect(200)
157 .expect('Content-Type', /json/)
158}
159
11474c3c
C
160function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
161 const path = '/api/v1/users/me/videos'
162
163 const req = request(url)
164 .get(path)
165 .query({ start: start })
166 .query({ count: count })
167
168 if (sort) req.query({ sort })
169
170 return req.set('Accept', 'application/json')
171 .set('Authorization', 'Bearer ' + accessToken)
172 .expect(200)
173 .expect('Content-Type', /json/)
174}
175
d525fc39
C
176function getAccountVideos (
177 url: string,
178 accessToken: string,
179 accountName: string,
180 start: number,
181 count: number,
182 sort?: string,
183 query: { nsfw?: boolean } = {}
184) {
ad9e39fb 185 const path = '/api/v1/accounts/' + accountName + '/videos'
6b738c7a
C
186
187 return makeGetRequest({
188 url,
189 path,
d525fc39 190 query: immutableAssign(query, {
6b738c7a
C
191 start,
192 count,
193 sort
d525fc39 194 }),
6b738c7a
C
195 token: accessToken,
196 statusCodeExpected: 200
197 })
198}
199
200function getVideoChannelVideos (
201 url: string,
202 accessToken: string,
8a19bee1 203 videoChannelName: string,
6b738c7a
C
204 start: number,
205 count: number,
d525fc39
C
206 sort?: string,
207 query: { nsfw?: boolean } = {}
6b738c7a 208) {
8a19bee1 209 const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
6b738c7a
C
210
211 return makeGetRequest({
212 url,
213 path,
d525fc39 214 query: immutableAssign(query, {
6b738c7a
C
215 start,
216 count,
217 sort
d525fc39 218 }),
6b738c7a
C
219 token: accessToken,
220 statusCodeExpected: 200
221 })
222}
223
0e1dc3e7
C
224function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
225 const path = '/api/v1/videos'
226
227 const req = request(url)
228 .get(path)
229 .query({ start: start })
230 .query({ count: count })
231
232 if (sort) req.query({ sort })
233
234 return req.set('Accept', 'application/json')
235 .expect(200)
236 .expect('Content-Type', /json/)
237}
238
239function getVideosListSort (url: string, sort: string) {
240 const path = '/api/v1/videos'
241
242 return request(url)
243 .get(path)
244 .query({ sort: sort })
245 .set('Accept', 'application/json')
246 .expect(200)
247 .expect('Content-Type', /json/)
248}
249
d525fc39 250function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) {
0e1dc3e7
C
251 const path = '/api/v1/videos'
252
253 return request(url)
57c36b27 254 .get(path)
d525fc39 255 .query(query)
f3aaa9a9 256 .set('Accept', 'application/json')
d525fc39 257 .expect(200)
f3aaa9a9 258 .expect('Content-Type', /json/)
0e1dc3e7
C
259}
260
d525fc39 261function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
0883b324 262 const path = '/api/v1/videos'
0e1dc3e7
C
263
264 return request(url)
d525fc39 265 .delete(path + '/' + id)
0e1dc3e7 266 .set('Accept', 'application/json')
d525fc39
C
267 .set('Authorization', 'Bearer ' + token)
268 .expect(expectedStatus)
0e1dc3e7
C
269}
270
25378bc8
C
271async function checkVideoFilesWereRemoved (
272 videoUUID: string,
273 serverNumber: number,
274 directories = [ 'videos', 'thumbnails', 'torrents', 'previews', 'captions' ]
275) {
f05a1c30
C
276 const testDirectory = 'test' + serverNumber
277
25378bc8 278 for (const directory of directories) {
f05a1c30
C
279 const directoryPath = join(root(), testDirectory, directory)
280
281 const directoryExists = existsSync(directoryPath)
282 expect(directoryExists).to.be.true
283
62689b94 284 const files = await readdir(directoryPath)
f05a1c30
C
285 for (const file of files) {
286 expect(file).to.not.contain(videoUUID)
287 }
288 }
289}
290
e11f68a3 291async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
e95561cd 292 const path = '/api/v1/videos/upload'
5f04dd2f
C
293 let defaultChannelId = '1'
294
295 try {
296 const res = await getMyUserInformation(url, accessToken)
297 defaultChannelId = res.body.videoChannels[0].id
298 } catch (e) { /* empty */ }
0e1dc3e7 299
ac81d1a0
C
300 // Override default attributes
301 const attributes = Object.assign({
0e1dc3e7
C
302 name: 'my super video',
303 category: 5,
304 licence: 4,
9d3ef9fe 305 language: 'zh',
5f04dd2f 306 channelId: defaultChannelId,
0e1dc3e7 307 nsfw: true,
2186386c 308 waitTranscoding: false,
0e1dc3e7 309 description: 'my super description',
2422c46b 310 support: 'my super support text',
0e1dc3e7 311 tags: [ 'tag' ],
11474c3c 312 privacy: VideoPrivacy.PUBLIC,
47564bbe 313 commentsEnabled: true,
156c50af 314 downloadingEnabled: true,
0e1dc3e7 315 fixture: 'video_short.webm'
ac81d1a0 316 }, videoAttributesArg)
0e1dc3e7
C
317
318 const req = request(url)
319 .post(path)
320 .set('Accept', 'application/json')
321 .set('Authorization', 'Bearer ' + accessToken)
322 .field('name', attributes.name)
0e1dc3e7 323 .field('nsfw', JSON.stringify(attributes.nsfw))
47564bbe 324 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
156c50af 325 .field('downloadingEnabled', JSON.stringify(attributes.downloadingEnabled))
2186386c 326 .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
11474c3c 327 .field('privacy', attributes.privacy.toString())
5f04dd2f 328 .field('channelId', attributes.channelId)
0e1dc3e7 329
a87d467a
C
330 if (attributes.description !== undefined) {
331 req.field('description', attributes.description)
332 }
8df87ce7
C
333 if (attributes.language !== undefined) {
334 req.field('language', attributes.language.toString())
335 }
a7fea183
C
336 if (attributes.category !== undefined) {
337 req.field('category', attributes.category.toString())
338 }
339 if (attributes.licence !== undefined) {
340 req.field('licence', attributes.licence.toString())
341 }
8df87ce7 342
2422c46b
C
343 for (let i = 0; i < attributes.tags.length; i++) {
344 req.field('tags[' + i + ']', attributes.tags[i])
345 }
346
ac81d1a0
C
347 if (attributes.thumbnailfile !== undefined) {
348 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
349 }
350 if (attributes.previewfile !== undefined) {
351 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
0e1dc3e7
C
352 }
353
2baea0c7
C
354 if (attributes.scheduleUpdate) {
355 req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
356
357 if (attributes.scheduleUpdate.privacy) {
358 req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
359 }
360 }
361
ac81d1a0 362 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
0e1dc3e7
C
363 .expect(specialStatus)
364}
365
ac81d1a0 366function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
0e1dc3e7
C
367 const path = '/api/v1/videos/' + id
368 const body = {}
369
370 if (attributes.name) body['name'] = attributes.name
371 if (attributes.category) body['category'] = attributes.category
372 if (attributes.licence) body['licence'] = attributes.licence
373 if (attributes.language) body['language'] = attributes.language
47564bbe
C
374 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
375 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
156c50af 376 if (attributes.downloadingEnabled !== undefined) body['downloadingEnabled'] = JSON.stringify(attributes.downloadingEnabled)
0e1dc3e7
C
377 if (attributes.description) body['description'] = attributes.description
378 if (attributes.tags) body['tags'] = attributes.tags
11474c3c 379 if (attributes.privacy) body['privacy'] = attributes.privacy
0f320037 380 if (attributes.channelId) body['channelId'] = attributes.channelId
2baea0c7 381 if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
0e1dc3e7 382
ac81d1a0
C
383 // Upload request
384 if (attributes.thumbnailfile || attributes.previewfile) {
385 const attaches: any = {}
386 if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
387 if (attributes.previewfile) attaches.previewfile = attributes.previewfile
388
389 return makeUploadRequest({
390 url,
391 method: 'PUT',
392 path,
393 token: accessToken,
394 fields: body,
395 attaches,
396 statusCodeExpected
397 })
398 }
399
400 return makePutBodyRequest({
401 url,
402 path,
403 fields: body,
404 token: accessToken,
405 statusCodeExpected
406 })
0e1dc3e7
C
407}
408
409function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
410 const path = '/api/v1/videos/' + id + '/rate'
411
412 return request(url)
413 .put(path)
414 .set('Accept', 'application/json')
415 .set('Authorization', 'Bearer ' + accessToken)
416 .send({ rating })
417 .expect(specialStatus)
418}
419
14d3270f 420function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
fdbda9e3 421 return new Promise<any>((res, rej) => {
14d3270f 422 const torrentName = videoUUID + '-' + resolution + '.torrent'
331128ed 423 const torrentPath = join(__dirname, '..', '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
fdbda9e3
C
424 readFile(torrentPath, (err, data) => {
425 if (err) return rej(err)
426
427 return res(parseTorrent(data))
428 })
429 })
430}
431
a20399c9
C
432async function completeVideoCheck (
433 url: string,
434 video: any,
435 attributes: {
436 name: string
437 category: number
438 licence: number
9d3ef9fe 439 language: string
a20399c9 440 nsfw: boolean
47564bbe 441 commentsEnabled: boolean
156c50af 442 downloadingEnabled: boolean
a20399c9 443 description: string
53a61317 444 publishedAt?: string
2422c46b 445 support: string
b64c950a
C
446 account: {
447 name: string
448 host: string
449 }
f6eebcb3
C
450 isLocal: boolean
451 tags: string[]
452 privacy: number
453 likes?: number
454 dislikes?: number
455 duration: number
a20399c9 456 channel: {
f6eebcb3
C
457 displayName: string
458 name: string
b1f5b93e 459 description
a20399c9
C
460 isLocal: boolean
461 }
f6eebcb3 462 fixture: string
a20399c9
C
463 files: {
464 resolution: number
465 size: number
ac81d1a0
C
466 }[],
467 thumbnailfile?: string
468 previewfile?: string
a20399c9
C
469 }
470) {
b1f5b93e
C
471 if (!attributes.likes) attributes.likes = 0
472 if (!attributes.dislikes) attributes.dislikes = 0
473
a20399c9 474 expect(video.name).to.equal(attributes.name)
09700934 475 expect(video.category.id).to.equal(attributes.category)
9d3ef9fe 476 expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
09700934 477 expect(video.licence.id).to.equal(attributes.licence)
9d3ef9fe 478 expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
09700934 479 expect(video.language.id).to.equal(attributes.language)
9d3ef9fe 480 expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
2243730c
C
481 expect(video.privacy.id).to.deep.equal(attributes.privacy)
482 expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
a20399c9
C
483 expect(video.nsfw).to.equal(attributes.nsfw)
484 expect(video.description).to.equal(attributes.description)
03e12d7c
C
485 expect(video.account.id).to.be.a('number')
486 expect(video.account.uuid).to.be.a('string')
b64c950a
C
487 expect(video.account.host).to.equal(attributes.account.host)
488 expect(video.account.name).to.equal(attributes.account.name)
f6eebcb3
C
489 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
490 expect(video.channel.name).to.equal(attributes.channel.name)
b1f5b93e
C
491 expect(video.likes).to.equal(attributes.likes)
492 expect(video.dislikes).to.equal(attributes.dislikes)
a20399c9 493 expect(video.isLocal).to.equal(attributes.isLocal)
b1f5b93e 494 expect(video.duration).to.equal(attributes.duration)
a20399c9 495 expect(dateIsValid(video.createdAt)).to.be.true
c49db162 496 expect(dateIsValid(video.publishedAt)).to.be.true
a20399c9
C
497 expect(dateIsValid(video.updatedAt)).to.be.true
498
53a61317 499 if (attributes.publishedAt) {
53a61317
C
500 expect(video.publishedAt).to.equal(attributes.publishedAt)
501 }
502
66b16caf 503 const res = await getVideo(url, video.uuid)
0f320037 504 const videoDetails: VideoDetails = res.body
a20399c9
C
505
506 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
507 expect(videoDetails.tags).to.deep.equal(attributes.tags)
19a3b914
C
508 expect(videoDetails.account.name).to.equal(attributes.account.name)
509 expect(videoDetails.account.host).to.equal(attributes.account.host)
f6eebcb3
C
510 expect(video.channel.displayName).to.equal(attributes.channel.displayName)
511 expect(video.channel.name).to.equal(attributes.channel.name)
0f320037 512 expect(videoDetails.channel.host).to.equal(attributes.account.host)
a20399c9 513 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
0f320037
C
514 expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
515 expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
516 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
156c50af 517 expect(videoDetails.downloadingEnabled).to.equal(attributes.downloadingEnabled)
a20399c9
C
518
519 for (const attributeFile of attributes.files) {
5d00a3d7 520 const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
a20399c9
C
521 expect(file).not.to.be.undefined
522
b1f5b93e
C
523 let extension = extname(attributes.fixture)
524 // Transcoding enabled on server 2, extension will always be .mp4
b64c950a 525 if (attributes.account.host === 'localhost:9002') extension = '.mp4'
b1f5b93e 526
a20399c9
C
527 const magnetUri = file.magnetUri
528 expect(file.magnetUri).to.have.lengthOf.above(2)
5d00a3d7
C
529 expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
530 expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
09700934
C
531 expect(file.resolution.id).to.equal(attributeFile.resolution)
532 expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
b1f5b93e
C
533
534 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
535 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
4176e227 536 expect(file.size,
7160878c 537 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')')
4176e227 538 .to.be.above(minSize).and.below(maxSize)
a20399c9 539
ac81d1a0 540 {
7b0956ec 541 await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
ac81d1a0
C
542 }
543
544 if (attributes.previewfile) {
7b0956ec 545 await testImage(url, attributes.previewfile, videoDetails.previewPath)
ac81d1a0 546 }
a20399c9
C
547
548 const torrent = await webtorrentAdd(magnetUri, true)
549 expect(torrent.files).to.be.an('array')
550 expect(torrent.files.length).to.equal(1)
551 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
552 }
553}
554
0e1dc3e7
C
555// ---------------------------------------------------------------------------
556
557export {
9567011b 558 getVideoDescription,
0e1dc3e7
C
559 getVideoCategories,
560 getVideoLicences,
11474c3c 561 getVideoPrivacies,
0e1dc3e7 562 getVideoLanguages,
11474c3c 563 getMyVideos,
6b738c7a
C
564 getAccountVideos,
565 getVideoChannelVideos,
0e1dc3e7 566 getVideo,
11474c3c 567 getVideoWithToken,
0e1dc3e7
C
568 getVideosList,
569 getVideosListPagination,
570 getVideosListSort,
571 removeVideo,
0883b324 572 getVideosListWithToken,
0e1dc3e7 573 uploadVideo,
d525fc39 574 getVideosWithFilters,
0e1dc3e7 575 updateVideo,
fdbda9e3 576 rateVideo,
1f3e9fec 577 viewVideo,
a20399c9 578 parseTorrentVideo,
066e94c5 579 getLocalVideos,
f05a1c30
C
580 completeVideoCheck,
581 checkVideoFilesWereRemoved
0e1dc3e7 582}