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