]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/utils/videos/videos.ts
Begin import script with youtube-dl
[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, isAbsolute, join } from 'path'
7 import * as request from 'supertest'
8 import { getMyUserInformation, makeGetRequest, root, ServerInfo, testImage } from '../'
9 import { VideoPrivacy } from '../../../../shared/models/videos'
10 import { readdirPromise } 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 checkVideoFilesWereRemoved (videoUUID: string, serverNumber: number) {
207 const testDirectory = 'test' + serverNumber
208
209 for (const directory of [ 'videos', 'thumbnails', 'torrents', 'previews' ]) {
210 const directoryPath = join(root(), testDirectory, directory)
211
212 const directoryExists = existsSync(directoryPath)
213 expect(directoryExists).to.be.true
214
215 const files = await readdirPromise(directoryPath)
216 for (const file of files) {
217 expect(file).to.not.contain(videoUUID)
218 }
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('nsfw', JSON.stringify(attributes.nsfw))
253 .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
254 .field('description', attributes.description)
255 .field('privacy', attributes.privacy.toString())
256 .field('channelId', attributes.channelId)
257
258 if (attributes.language !== undefined) {
259 req.field('language', attributes.language.toString())
260 }
261 if (attributes.category !== undefined) {
262 req.field('category', attributes.category.toString())
263 }
264 if (attributes.licence !== undefined) {
265 req.field('licence', attributes.licence.toString())
266 }
267
268 for (let i = 0; i < attributes.tags.length; i++) {
269 req.field('tags[' + i + ']', attributes.tags[i])
270 }
271
272 let filePath = ''
273 if (isAbsolute(attributes.fixture)) {
274 filePath = attributes.fixture
275 } else {
276 filePath = join(__dirname, '..', '..', 'api', 'fixtures', attributes.fixture)
277 }
278
279 return req.attach('videofile', filePath)
280 .expect(specialStatus)
281 }
282
283 function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, specialStatus = 204) {
284 const path = '/api/v1/videos/' + id
285 const body = {}
286
287 if (attributes.name) body['name'] = attributes.name
288 if (attributes.category) body['category'] = attributes.category
289 if (attributes.licence) body['licence'] = attributes.licence
290 if (attributes.language) body['language'] = attributes.language
291 if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
292 if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
293 if (attributes.description) body['description'] = attributes.description
294 if (attributes.tags) body['tags'] = attributes.tags
295 if (attributes.privacy) body['privacy'] = attributes.privacy
296
297 return request(url)
298 .put(path)
299 .send(body)
300 .set('Accept', 'application/json')
301 .set('Authorization', 'Bearer ' + accessToken)
302 .expect(specialStatus)
303 }
304
305 function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
306 const path = '/api/v1/videos/' + id + '/rate'
307
308 return request(url)
309 .put(path)
310 .set('Accept', 'application/json')
311 .set('Authorization', 'Bearer ' + accessToken)
312 .send({ rating })
313 .expect(specialStatus)
314 }
315
316 function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
317 return new Promise<any>((res, rej) => {
318 const torrentName = videoUUID + '-' + resolution + '.torrent'
319 const torrentPath = join(__dirname, '..', '..', '..', '..', 'test' + server.serverNumber, 'torrents', torrentName)
320 readFile(torrentPath, (err, data) => {
321 if (err) return rej(err)
322
323 return res(parseTorrent(data))
324 })
325 })
326 }
327
328 async function completeVideoCheck (
329 url: string,
330 video: any,
331 attributes: {
332 name: string
333 category: number
334 licence: number
335 language: number
336 nsfw: boolean
337 commentsEnabled: boolean
338 description: string
339 host: string
340 account: string
341 isLocal: boolean,
342 tags: string[],
343 privacy: number,
344 likes?: number,
345 dislikes?: number,
346 duration: number,
347 channel: {
348 name: string,
349 description
350 isLocal: boolean
351 }
352 fixture: string,
353 files: {
354 resolution: number
355 size: number
356 }[]
357 }
358 ) {
359 if (!attributes.likes) attributes.likes = 0
360 if (!attributes.dislikes) attributes.dislikes = 0
361
362 expect(video.name).to.equal(attributes.name)
363 expect(video.category).to.equal(attributes.category)
364 expect(video.categoryLabel).to.equal(VIDEO_CATEGORIES[attributes.category] || 'Misc')
365 expect(video.licence).to.equal(attributes.licence)
366 expect(video.licenceLabel).to.equal(VIDEO_LICENCES[attributes.licence] || 'Unknown')
367 expect(video.language).to.equal(attributes.language)
368 expect(video.languageLabel).to.equal(VIDEO_LANGUAGES[attributes.language] || 'Unknown')
369 expect(video.nsfw).to.equal(attributes.nsfw)
370 expect(video.description).to.equal(attributes.description)
371 expect(video.serverHost).to.equal(attributes.host)
372 expect(video.accountName).to.equal(attributes.account)
373 expect(video.likes).to.equal(attributes.likes)
374 expect(video.dislikes).to.equal(attributes.dislikes)
375 expect(video.isLocal).to.equal(attributes.isLocal)
376 expect(video.duration).to.equal(attributes.duration)
377 expect(dateIsValid(video.createdAt)).to.be.true
378 expect(dateIsValid(video.updatedAt)).to.be.true
379
380 const res = await getVideo(url, video.uuid)
381 const videoDetails = res.body
382
383 expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
384 expect(videoDetails.tags).to.deep.equal(attributes.tags)
385 expect(videoDetails.privacy).to.deep.equal(attributes.privacy)
386 expect(videoDetails.privacyLabel).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
387 expect(videoDetails.account.name).to.equal(attributes.account)
388 expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
389
390 expect(videoDetails.channel.displayName).to.equal(attributes.channel.name)
391 expect(videoDetails.channel.name).to.have.lengthOf(36)
392 expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
393 expect(dateIsValid(videoDetails.channel.createdAt)).to.be.true
394 expect(dateIsValid(videoDetails.channel.updatedAt)).to.be.true
395
396 for (const attributeFile of attributes.files) {
397 const file = videoDetails.files.find(f => f.resolution === attributeFile.resolution)
398 expect(file).not.to.be.undefined
399
400 let extension = extname(attributes.fixture)
401 // Transcoding enabled on server 2, extension will always be .mp4
402 if (attributes.host === 'localhost:9002') extension = '.mp4'
403
404 const magnetUri = file.magnetUri
405 expect(file.magnetUri).to.have.lengthOf.above(2)
406 expect(file.torrentUrl).to.equal(`http://${attributes.host}/static/torrents/${videoDetails.uuid}-${file.resolution}.torrent`)
407 expect(file.fileUrl).to.equal(`http://${attributes.host}/static/webseed/${videoDetails.uuid}-${file.resolution}${extension}`)
408 expect(file.resolution).to.equal(attributeFile.resolution)
409 expect(file.resolutionLabel).to.equal(attributeFile.resolution + 'p')
410
411 const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
412 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
413 expect(file.size).to.be.above(minSize).and.below(maxSize)
414
415 const test = await testImage(url, attributes.fixture, videoDetails.thumbnailPath)
416 expect(test).to.equal(true)
417
418 const torrent = await webtorrentAdd(magnetUri, true)
419 expect(torrent.files).to.be.an('array')
420 expect(torrent.files.length).to.equal(1)
421 expect(torrent.files[0].path).to.exist.and.to.not.equal('')
422 }
423 }
424
425 // ---------------------------------------------------------------------------
426
427 export {
428 getVideoDescription,
429 getVideoCategories,
430 getVideoLicences,
431 getVideoPrivacies,
432 getVideoLanguages,
433 getMyVideos,
434 getVideo,
435 getVideoWithToken,
436 getVideosList,
437 getVideosListPagination,
438 getVideosListSort,
439 removeVideo,
440 searchVideo,
441 searchVideoWithPagination,
442 searchVideoWithSort,
443 uploadVideo,
444 updateVideo,
445 rateVideo,
446 viewVideo,
447 parseTorrentVideo,
448 completeVideoCheck,
449 checkVideoFilesWereRemoved
450 }