]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/videos-common-filters.ts
Fix CI timeout
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / videos-common-filters.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { expect } from 'chai'
4 import { pick } from '@shared/core-utils'
5 import { HttpStatusCode, UserRole, Video, VideoDetails, VideoInclude, VideoPrivacy } from '@shared/models'
6 import {
7 cleanupTests,
8 createMultipleServers,
9 doubleFollow,
10 makeGetRequest,
11 PeerTubeServer,
12 setAccessTokensToServers,
13 setDefaultAccountAvatar,
14 setDefaultVideoChannel,
15 waitJobs
16 } from '@shared/server-commands'
17
18 describe('Test videos filter', function () {
19 let servers: PeerTubeServer[]
20 let paths: string[]
21 let remotePaths: string[]
22
23 // ---------------------------------------------------------------
24
25 before(async function () {
26 this.timeout(240000)
27
28 servers = await createMultipleServers(2)
29
30 await setAccessTokensToServers(servers)
31 await setDefaultVideoChannel(servers)
32 await setDefaultAccountAvatar(servers)
33
34 await servers[1].config.enableMinimumTranscoding()
35
36 for (const server of servers) {
37 const moderator = { username: 'moderator', password: 'my super password' }
38 await server.users.create({ username: moderator.username, password: moderator.password, role: UserRole.MODERATOR })
39 server['moderatorAccessToken'] = await server.login.getAccessToken(moderator)
40
41 await server.videos.upload({ attributes: { name: 'public ' + server.serverNumber } })
42
43 {
44 const attributes = { name: 'unlisted ' + server.serverNumber, privacy: VideoPrivacy.UNLISTED }
45 await server.videos.upload({ attributes })
46 }
47
48 {
49 const attributes = { name: 'private ' + server.serverNumber, privacy: VideoPrivacy.PRIVATE }
50 await server.videos.upload({ attributes })
51 }
52 }
53
54 await doubleFollow(servers[0], servers[1])
55
56 paths = [
57 `/api/v1/video-channels/root_channel/videos`,
58 `/api/v1/accounts/root/videos`,
59 '/api/v1/videos',
60 '/api/v1/search/videos'
61 ]
62
63 remotePaths = [
64 `/api/v1/video-channels/root_channel@${servers[1].host}/videos`,
65 `/api/v1/accounts/root@${servers[1].host}/videos`,
66 '/api/v1/videos',
67 '/api/v1/search/videos'
68 ]
69 })
70
71 describe('Check deprecated videos filter', function () {
72
73 async function getVideosNames (server: PeerTubeServer, token: string, filter: string, expectedStatus = HttpStatusCode.OK_200) {
74 const videosResults: Video[][] = []
75
76 for (const path of paths) {
77 const res = await makeGetRequest({
78 url: server.url,
79 path,
80 token,
81 query: {
82 sort: 'createdAt',
83 filter
84 },
85 expectedStatus
86 })
87
88 videosResults.push(res.body.data.map(v => v.name))
89 }
90
91 return videosResults
92 }
93
94 it('Should display local videos', async function () {
95 for (const server of servers) {
96 const namesResults = await getVideosNames(server, server.accessToken, 'local')
97 for (const names of namesResults) {
98 expect(names).to.have.lengthOf(1)
99 expect(names[0]).to.equal('public ' + server.serverNumber)
100 }
101 }
102 })
103
104 it('Should display all local videos by the admin or the moderator', async function () {
105 for (const server of servers) {
106 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
107
108 const namesResults = await getVideosNames(server, token, 'all-local')
109 for (const names of namesResults) {
110 expect(names).to.have.lengthOf(3)
111
112 expect(names[0]).to.equal('public ' + server.serverNumber)
113 expect(names[1]).to.equal('unlisted ' + server.serverNumber)
114 expect(names[2]).to.equal('private ' + server.serverNumber)
115 }
116 }
117 }
118 })
119
120 it('Should display all videos by the admin or the moderator', async function () {
121 for (const server of servers) {
122 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
123
124 const [ channelVideos, accountVideos, videos, searchVideos ] = await getVideosNames(server, token, 'all')
125 expect(channelVideos).to.have.lengthOf(3)
126 expect(accountVideos).to.have.lengthOf(3)
127
128 expect(videos).to.have.lengthOf(5)
129 expect(searchVideos).to.have.lengthOf(5)
130 }
131 }
132 })
133 })
134
135 describe('Check videos filters', function () {
136
137 async function listVideos (options: {
138 server: PeerTubeServer
139 path: string
140 isLocal?: boolean
141 hasWebtorrentFiles?: boolean
142 hasHLSFiles?: boolean
143 include?: VideoInclude
144 privacyOneOf?: VideoPrivacy[]
145 category?: number
146 tagsAllOf?: string[]
147 token?: string
148 expectedStatus?: HttpStatusCode
149 }) {
150 const res = await makeGetRequest({
151 url: options.server.url,
152 path: options.path,
153 token: options.token ?? options.server.accessToken,
154 query: {
155 ...pick(options, [ 'isLocal', 'include', 'category', 'tagsAllOf', 'hasWebtorrentFiles', 'hasHLSFiles', 'privacyOneOf' ]),
156
157 sort: 'createdAt'
158 },
159 expectedStatus: options.expectedStatus ?? HttpStatusCode.OK_200
160 })
161
162 return res.body.data as Video[]
163 }
164
165 async function getVideosNames (options: {
166 server: PeerTubeServer
167 isLocal?: boolean
168 include?: VideoInclude
169 privacyOneOf?: VideoPrivacy[]
170 token?: string
171 expectedStatus?: HttpStatusCode
172 }) {
173 const videosResults: string[][] = []
174
175 for (const path of paths) {
176 const videos = await listVideos({ ...options, path })
177
178 videosResults.push(videos.map(v => v.name))
179 }
180
181 return videosResults
182 }
183
184 it('Should display local videos', async function () {
185 for (const server of servers) {
186 const namesResults = await getVideosNames({ server, isLocal: true })
187
188 for (const names of namesResults) {
189 expect(names).to.have.lengthOf(1)
190 expect(names[0]).to.equal('public ' + server.serverNumber)
191 }
192 }
193 })
194
195 it('Should display local videos with hidden privacy by the admin or the moderator', async function () {
196 for (const server of servers) {
197 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
198
199 const namesResults = await getVideosNames({
200 server,
201 token,
202 isLocal: true,
203 privacyOneOf: [ VideoPrivacy.UNLISTED, VideoPrivacy.PUBLIC, VideoPrivacy.PRIVATE ]
204 })
205
206 for (const names of namesResults) {
207 expect(names).to.have.lengthOf(3)
208
209 expect(names[0]).to.equal('public ' + server.serverNumber)
210 expect(names[1]).to.equal('unlisted ' + server.serverNumber)
211 expect(names[2]).to.equal('private ' + server.serverNumber)
212 }
213 }
214 }
215 })
216
217 it('Should display all videos by the admin or the moderator', async function () {
218 for (const server of servers) {
219 for (const token of [ server.accessToken, server['moderatorAccessToken'] ]) {
220
221 const [ channelVideos, accountVideos, videos, searchVideos ] = await getVideosNames({
222 server,
223 token,
224 privacyOneOf: [ VideoPrivacy.UNLISTED, VideoPrivacy.PUBLIC, VideoPrivacy.PRIVATE ]
225 })
226
227 expect(channelVideos).to.have.lengthOf(3)
228 expect(accountVideos).to.have.lengthOf(3)
229
230 expect(videos).to.have.lengthOf(5)
231 expect(searchVideos).to.have.lengthOf(5)
232 }
233 }
234 })
235
236 it('Should display only remote videos', async function () {
237 this.timeout(120000)
238
239 await servers[1].videos.upload({ attributes: { name: 'remote video' } })
240
241 await waitJobs(servers)
242
243 const finder = (videos: Video[]) => videos.find(v => v.name === 'remote video')
244
245 for (const path of remotePaths) {
246 {
247 const videos = await listVideos({ server: servers[0], path })
248 const video = finder(videos)
249 expect(video).to.exist
250 }
251
252 {
253 const videos = await listVideos({ server: servers[0], path, isLocal: false })
254 const video = finder(videos)
255 expect(video).to.exist
256 }
257
258 {
259 const videos = await listVideos({ server: servers[0], path, isLocal: true })
260 const video = finder(videos)
261 expect(video).to.not.exist
262 }
263 }
264 })
265
266 it('Should include not published videos', async function () {
267 await servers[0].config.enableLive({ allowReplay: false, transcoding: false })
268 await servers[0].live.create({ fields: { name: 'live video', channelId: servers[0].store.channel.id, privacy: VideoPrivacy.PUBLIC } })
269
270 const finder = (videos: Video[]) => videos.find(v => v.name === 'live video')
271
272 for (const path of paths) {
273 {
274 const videos = await listVideos({ server: servers[0], path })
275 const video = finder(videos)
276 expect(video).to.not.exist
277 expect(videos[0].state).to.not.exist
278 expect(videos[0].waitTranscoding).to.not.exist
279 }
280
281 {
282 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.NOT_PUBLISHED_STATE })
283 const video = finder(videos)
284 expect(video).to.exist
285 expect(video.state).to.exist
286 }
287 }
288 })
289
290 it('Should include blacklisted videos', async function () {
291 const { id } = await servers[0].videos.upload({ attributes: { name: 'blacklisted' } })
292
293 await servers[0].blacklist.add({ videoId: id })
294
295 const finder = (videos: Video[]) => videos.find(v => v.name === 'blacklisted')
296
297 for (const path of paths) {
298 {
299 const videos = await listVideos({ server: servers[0], path })
300 const video = finder(videos)
301 expect(video).to.not.exist
302 expect(videos[0].blacklisted).to.not.exist
303 }
304
305 {
306 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.BLACKLISTED })
307 const video = finder(videos)
308 expect(video).to.exist
309 expect(video.blacklisted).to.be.true
310 }
311 }
312 })
313
314 it('Should include videos from muted account', async function () {
315 const finder = (videos: Video[]) => videos.find(v => v.name === 'remote video')
316
317 await servers[0].blocklist.addToServerBlocklist({ account: 'root@' + servers[1].host })
318
319 for (const path of remotePaths) {
320 {
321 const videos = await listVideos({ server: servers[0], path })
322 const video = finder(videos)
323 expect(video).to.not.exist
324
325 // Some paths won't have videos
326 if (videos[0]) {
327 expect(videos[0].blockedOwner).to.not.exist
328 expect(videos[0].blockedServer).to.not.exist
329 }
330 }
331
332 {
333 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.BLOCKED_OWNER })
334
335 const video = finder(videos)
336 expect(video).to.exist
337 expect(video.blockedServer).to.be.false
338 expect(video.blockedOwner).to.be.true
339 }
340 }
341
342 await servers[0].blocklist.removeFromServerBlocklist({ account: 'root@' + servers[1].host })
343 })
344
345 it('Should include videos from muted server', async function () {
346 const finder = (videos: Video[]) => videos.find(v => v.name === 'remote video')
347
348 await servers[0].blocklist.addToServerBlocklist({ server: servers[1].host })
349
350 for (const path of remotePaths) {
351 {
352 const videos = await listVideos({ server: servers[0], path })
353 const video = finder(videos)
354 expect(video).to.not.exist
355
356 // Some paths won't have videos
357 if (videos[0]) {
358 expect(videos[0].blockedOwner).to.not.exist
359 expect(videos[0].blockedServer).to.not.exist
360 }
361 }
362
363 {
364 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.BLOCKED_OWNER })
365 const video = finder(videos)
366 expect(video).to.exist
367 expect(video.blockedServer).to.be.true
368 expect(video.blockedOwner).to.be.false
369 }
370 }
371
372 await servers[0].blocklist.removeFromServerBlocklist({ server: servers[1].host })
373 })
374
375 it('Should include video files', async function () {
376 for (const path of paths) {
377 {
378 const videos = await listVideos({ server: servers[0], path })
379
380 for (const video of videos) {
381 const videoWithFiles = video as VideoDetails
382
383 expect(videoWithFiles.files).to.not.exist
384 expect(videoWithFiles.streamingPlaylists).to.not.exist
385 }
386 }
387
388 {
389 const videos = await listVideos({ server: servers[0], path, include: VideoInclude.FILES })
390
391 for (const video of videos) {
392 const videoWithFiles = video as VideoDetails
393
394 expect(videoWithFiles.files).to.exist
395 expect(videoWithFiles.files).to.have.length.at.least(1)
396 }
397 }
398 }
399 })
400
401 it('Should filter by tags and category', async function () {
402 await servers[0].videos.upload({ attributes: { name: 'tag filter', tags: [ 'tag1', 'tag2' ] } })
403 await servers[0].videos.upload({ attributes: { name: 'tag filter with category', tags: [ 'tag3' ], category: 4 } })
404
405 for (const path of paths) {
406 {
407 const videos = await listVideos({ server: servers[0], path, tagsAllOf: [ 'tag1', 'tag2' ] })
408 expect(videos).to.have.lengthOf(1)
409 expect(videos[0].name).to.equal('tag filter')
410 }
411
412 {
413 const videos = await listVideos({ server: servers[0], path, tagsAllOf: [ 'tag1', 'tag3' ] })
414 expect(videos).to.have.lengthOf(0)
415 }
416
417 {
418 const { data, total } = await servers[0].videos.list({ tagsAllOf: [ 'tag3' ], categoryOneOf: [ 4 ] })
419 expect(total).to.equal(1)
420 expect(data[0].name).to.equal('tag filter with category')
421 }
422
423 {
424 const { total } = await servers[0].videos.list({ tagsAllOf: [ 'tag4' ], categoryOneOf: [ 4 ] })
425 expect(total).to.equal(0)
426 }
427 }
428 })
429
430 it('Should filter by HLS or WebTorrent files', async function () {
431 this.timeout(360000)
432
433 const finderFactory = (name: string) => (videos: Video[]) => videos.some(v => v.name === name)
434
435 await servers[0].config.enableTranscoding(true, false)
436 await servers[0].videos.upload({ attributes: { name: 'webtorrent video' } })
437 const hasWebtorrent = finderFactory('webtorrent video')
438
439 await waitJobs(servers)
440
441 await servers[0].config.enableTranscoding(false, true)
442 await servers[0].videos.upload({ attributes: { name: 'hls video' } })
443 const hasHLS = finderFactory('hls video')
444
445 await waitJobs(servers)
446
447 await servers[0].config.enableTranscoding(true, true)
448 await servers[0].videos.upload({ attributes: { name: 'hls and webtorrent video' } })
449 const hasBoth = finderFactory('hls and webtorrent video')
450
451 await waitJobs(servers)
452
453 for (const path of paths) {
454 {
455 const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: true })
456
457 expect(hasWebtorrent(videos)).to.be.true
458 expect(hasHLS(videos)).to.be.false
459 expect(hasBoth(videos)).to.be.true
460 }
461
462 {
463 const videos = await listVideos({ server: servers[0], path, hasWebtorrentFiles: false })
464
465 expect(hasWebtorrent(videos)).to.be.false
466 expect(hasHLS(videos)).to.be.true
467 expect(hasBoth(videos)).to.be.false
468 }
469
470 {
471 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true })
472
473 expect(hasWebtorrent(videos)).to.be.false
474 expect(hasHLS(videos)).to.be.true
475 expect(hasBoth(videos)).to.be.true
476 }
477
478 {
479 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false })
480
481 expect(hasWebtorrent(videos)).to.be.true
482 expect(hasHLS(videos)).to.be.false
483 expect(hasBoth(videos)).to.be.false
484 }
485
486 {
487 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: false, hasWebtorrentFiles: false })
488
489 expect(hasWebtorrent(videos)).to.be.false
490 expect(hasHLS(videos)).to.be.false
491 expect(hasBoth(videos)).to.be.false
492 }
493
494 {
495 const videos = await listVideos({ server: servers[0], path, hasHLSFiles: true, hasWebtorrentFiles: true })
496
497 expect(hasWebtorrent(videos)).to.be.false
498 expect(hasHLS(videos)).to.be.false
499 expect(hasBoth(videos)).to.be.true
500 }
501 }
502 })
503 })
504
505 after(async function () {
506 await cleanupTests(servers)
507 })
508 })