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