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