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