]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/video-playlists.ts
Fix filters on playlists
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / video-playlists.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import { expect } from 'chai'
4 import { checkPlaylistFilesWereRemoved, testImage } from '@server/tests/shared'
5 import { wait } from '@shared/core-utils'
6 import { uuidToShort } from '@shared/extra-utils'
7 import {
8 HttpStatusCode,
9 VideoPlaylist,
10 VideoPlaylistCreateResult,
11 VideoPlaylistElementType,
12 VideoPlaylistPrivacy,
13 VideoPlaylistType,
14 VideoPrivacy
15 } from '@shared/models'
16 import {
17 cleanupTests,
18 createMultipleServers,
19 doubleFollow,
20 PeerTubeServer,
21 PlaylistsCommand,
22 setAccessTokensToServers,
23 setDefaultAccountAvatar,
24 setDefaultVideoChannel,
25 waitJobs
26 } from '@shared/server-commands'
27
28 async function checkPlaylistElementType (
29 servers: PeerTubeServer[],
30 playlistId: string,
31 type: VideoPlaylistElementType,
32 position: number,
33 name: string,
34 total: number
35 ) {
36 for (const server of servers) {
37 const body = await server.playlists.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
38 expect(body.total).to.equal(total)
39
40 const videoElement = body.data.find(e => e.position === position)
41 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
42
43 if (type === VideoPlaylistElementType.REGULAR) {
44 expect(videoElement.video).to.not.be.null
45 expect(videoElement.video.name).to.equal(name)
46 } else {
47 expect(videoElement.video).to.be.null
48 }
49 }
50 }
51
52 describe('Test video playlists', function () {
53 let servers: PeerTubeServer[] = []
54
55 let playlistServer2Id1: number
56 let playlistServer2Id2: number
57 let playlistServer2UUID2: string
58
59 let playlistServer1Id: number
60 let playlistServer1DisplayName: string
61 let playlistServer1UUID: string
62 let playlistServer1UUID2: string
63
64 let playlistElementServer1Video4: number
65 let playlistElementServer1Video5: number
66 let playlistElementNSFW: number
67
68 let nsfwVideoServer1: number
69
70 let userTokenServer1: string
71
72 let commands: PlaylistsCommand[]
73
74 before(async function () {
75 this.timeout(240000)
76
77 servers = await createMultipleServers(3)
78
79 // Get the access tokens
80 await setAccessTokensToServers(servers)
81 await setDefaultVideoChannel(servers)
82 await setDefaultAccountAvatar(servers)
83
84 for (const server of servers) {
85 await server.config.disableTranscoding()
86 }
87
88 // Server 1 and server 2 follow each other
89 await doubleFollow(servers[0], servers[1])
90 // Server 1 and server 3 follow each other
91 await doubleFollow(servers[0], servers[2])
92
93 commands = servers.map(s => s.playlists)
94
95 {
96 servers[0].store.videos = []
97 servers[1].store.videos = []
98 servers[2].store.videos = []
99
100 for (const server of servers) {
101 for (let i = 0; i < 7; i++) {
102 const name = `video ${i} server ${server.serverNumber}`
103 const video = await server.videos.upload({ attributes: { name, nsfw: false } })
104
105 server.store.videos.push(video)
106 }
107 }
108 }
109
110 nsfwVideoServer1 = (await servers[0].videos.quickUpload({ name: 'NSFW video', nsfw: true })).id
111
112 userTokenServer1 = await servers[0].users.generateUserAndToken('user1')
113
114 await waitJobs(servers)
115 })
116
117 describe('Check playlists filters and privacies', function () {
118
119 it('Should list video playlist privacies', async function () {
120 const privacies = await commands[0].getPrivacies()
121
122 expect(Object.keys(privacies)).to.have.length.at.least(3)
123 expect(privacies[3]).to.equal('Private')
124 })
125
126 it('Should filter on playlist type', async function () {
127 this.timeout(30000)
128
129 const token = servers[0].accessToken
130
131 await commands[0].create({
132 attributes: {
133 displayName: 'my super playlist',
134 privacy: VideoPlaylistPrivacy.PUBLIC,
135 description: 'my super description',
136 thumbnailfile: 'thumbnail.jpg',
137 videoChannelId: servers[0].store.channel.id
138 }
139 })
140
141 {
142 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
143
144 expect(body.total).to.equal(1)
145 expect(body.data).to.have.lengthOf(1)
146
147 const playlist = body.data[0]
148 expect(playlist.displayName).to.equal('Watch later')
149 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
150 expect(playlist.type.label).to.equal('Watch later')
151 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
152 }
153
154 {
155 const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.WATCH_LATER })
156 const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.WATCH_LATER })
157
158 for (const body of [ bodyList, bodyChannel ]) {
159 expect(body.total).to.equal(0)
160 expect(body.data).to.have.lengthOf(0)
161 }
162 }
163
164 {
165 const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.REGULAR })
166 const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.REGULAR })
167
168 let playlist: VideoPlaylist = null
169 for (const body of [ bodyList, bodyChannel ]) {
170
171 expect(body.total).to.equal(1)
172 expect(body.data).to.have.lengthOf(1)
173
174 playlist = body.data[0]
175 expect(playlist.displayName).to.equal('my super playlist')
176 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
177 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
178 }
179
180 await commands[0].update({
181 playlistId: playlist.id,
182 attributes: {
183 privacy: VideoPlaylistPrivacy.PRIVATE
184 }
185 })
186 }
187
188 {
189 const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.REGULAR })
190 const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.REGULAR })
191
192 for (const body of [ bodyList, bodyChannel ]) {
193 expect(body.total).to.equal(0)
194 expect(body.data).to.have.lengthOf(0)
195 }
196 }
197
198 {
199 const body = await commands[0].listByAccount({ handle: 'root' })
200 expect(body.total).to.equal(0)
201 expect(body.data).to.have.lengthOf(0)
202 }
203 })
204
205 it('Should get private playlist for a classic user', async function () {
206 const token = await servers[0].users.generateUserAndToken('toto')
207
208 const body = await commands[0].listByAccount({ token, handle: 'toto' })
209
210 expect(body.total).to.equal(1)
211 expect(body.data).to.have.lengthOf(1)
212
213 const playlistId = body.data[0].id
214 await commands[0].listVideos({ token, playlistId })
215 })
216 })
217
218 describe('Create and federate playlists', function () {
219
220 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
221 this.timeout(30000)
222
223 await commands[0].create({
224 attributes: {
225 displayName: 'my super playlist',
226 privacy: VideoPlaylistPrivacy.PUBLIC,
227 description: 'my super description',
228 thumbnailfile: 'thumbnail.jpg',
229 videoChannelId: servers[0].store.channel.id
230 }
231 })
232
233 await waitJobs(servers)
234 // Processing a playlist by the receiver could be long
235 await wait(3000)
236
237 for (const server of servers) {
238 const body = await server.playlists.list({ start: 0, count: 5 })
239 expect(body.total).to.equal(1)
240 expect(body.data).to.have.lengthOf(1)
241
242 const playlistFromList = body.data[0]
243
244 const playlistFromGet = await server.playlists.get({ playlistId: playlistFromList.uuid })
245
246 for (const playlist of [ playlistFromGet, playlistFromList ]) {
247 expect(playlist.id).to.be.a('number')
248 expect(playlist.uuid).to.be.a('string')
249
250 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
251
252 expect(playlist.displayName).to.equal('my super playlist')
253 expect(playlist.description).to.equal('my super description')
254 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
255 expect(playlist.privacy.label).to.equal('Public')
256 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
257 expect(playlist.type.label).to.equal('Regular')
258 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
259
260 expect(playlist.videosLength).to.equal(0)
261
262 expect(playlist.ownerAccount.name).to.equal('root')
263 expect(playlist.ownerAccount.displayName).to.equal('root')
264 expect(playlist.videoChannel.name).to.equal('root_channel')
265 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
266 }
267 }
268 })
269
270 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
271 this.timeout(30000)
272
273 {
274 const playlist = await servers[1].playlists.create({
275 attributes: {
276 displayName: 'playlist 2',
277 privacy: VideoPlaylistPrivacy.PUBLIC,
278 videoChannelId: servers[1].store.channel.id
279 }
280 })
281 playlistServer2Id1 = playlist.id
282 }
283
284 {
285 const playlist = await servers[1].playlists.create({
286 attributes: {
287 displayName: 'playlist 3',
288 privacy: VideoPlaylistPrivacy.PUBLIC,
289 thumbnailfile: 'thumbnail.jpg',
290 videoChannelId: servers[1].store.channel.id
291 }
292 })
293
294 playlistServer2Id2 = playlist.id
295 playlistServer2UUID2 = playlist.uuid
296 }
297
298 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
299 await servers[1].playlists.addElement({
300 playlistId: id,
301 attributes: { videoId: servers[1].store.videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
302 })
303 await servers[1].playlists.addElement({
304 playlistId: id,
305 attributes: { videoId: servers[1].store.videos[1].id }
306 })
307 }
308
309 await waitJobs(servers)
310 await wait(3000)
311
312 for (const server of [ servers[0], servers[1] ]) {
313 const body = await server.playlists.list({ start: 0, count: 5 })
314
315 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
316 expect(playlist2).to.not.be.undefined
317 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
318
319 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
320 expect(playlist3).to.not.be.undefined
321 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
322 }
323
324 const body = await servers[2].playlists.list({ start: 0, count: 5 })
325 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
326 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
327 })
328
329 it('Should have the playlist on server 3 after a new follow', async function () {
330 this.timeout(30000)
331
332 // Server 2 and server 3 follow each other
333 await doubleFollow(servers[1], servers[2])
334
335 const body = await servers[2].playlists.list({ start: 0, count: 5 })
336
337 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
338 expect(playlist2).to.not.be.undefined
339 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
340
341 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
342 })
343 })
344
345 describe('List playlists', function () {
346
347 it('Should correctly list the playlists', async function () {
348 this.timeout(30000)
349
350 {
351 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: 'createdAt' })
352 expect(body.total).to.equal(3)
353
354 const data = body.data
355 expect(data).to.have.lengthOf(2)
356 expect(data[0].displayName).to.equal('playlist 2')
357 expect(data[1].displayName).to.equal('playlist 3')
358 }
359
360 {
361 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: '-createdAt' })
362 expect(body.total).to.equal(3)
363
364 const data = body.data
365 expect(data).to.have.lengthOf(2)
366 expect(data[0].displayName).to.equal('playlist 2')
367 expect(data[1].displayName).to.equal('my super playlist')
368 }
369 })
370
371 it('Should list video channel playlists', async function () {
372 this.timeout(30000)
373
374 {
375 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
376 expect(body.total).to.equal(1)
377
378 const data = body.data
379 expect(data).to.have.lengthOf(1)
380 expect(data[0].displayName).to.equal('my super playlist')
381 }
382 })
383
384 it('Should list account playlists', async function () {
385 this.timeout(30000)
386
387 {
388 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
389 expect(body.total).to.equal(2)
390
391 const data = body.data
392 expect(data).to.have.lengthOf(1)
393 expect(data[0].displayName).to.equal('playlist 2')
394 }
395
396 {
397 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
398 expect(body.total).to.equal(2)
399
400 const data = body.data
401 expect(data).to.have.lengthOf(1)
402 expect(data[0].displayName).to.equal('playlist 3')
403 }
404
405 {
406 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
407 expect(body.total).to.equal(1)
408
409 const data = body.data
410 expect(data).to.have.lengthOf(1)
411 expect(data[0].displayName).to.equal('playlist 3')
412 }
413
414 {
415 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
416 expect(body.total).to.equal(0)
417
418 const data = body.data
419 expect(data).to.have.lengthOf(0)
420 }
421 })
422 })
423
424 describe('Playlist rights', function () {
425 let unlistedPlaylist: VideoPlaylistCreateResult
426 let privatePlaylist: VideoPlaylistCreateResult
427
428 before(async function () {
429 this.timeout(30000)
430
431 {
432 unlistedPlaylist = await servers[1].playlists.create({
433 attributes: {
434 displayName: 'playlist unlisted',
435 privacy: VideoPlaylistPrivacy.UNLISTED,
436 videoChannelId: servers[1].store.channel.id
437 }
438 })
439 }
440
441 {
442 privatePlaylist = await servers[1].playlists.create({
443 attributes: {
444 displayName: 'playlist private',
445 privacy: VideoPlaylistPrivacy.PRIVATE
446 }
447 })
448 }
449
450 await waitJobs(servers)
451 await wait(3000)
452 })
453
454 it('Should not list unlisted or private playlists', async function () {
455 for (const server of servers) {
456 const results = [
457 await server.playlists.listByAccount({ handle: 'root@' + servers[1].host, sort: '-createdAt' }),
458 await server.playlists.list({ start: 0, count: 2, sort: '-createdAt' })
459 ]
460
461 expect(results[0].total).to.equal(2)
462 expect(results[1].total).to.equal(3)
463
464 for (const body of results) {
465 const data = body.data
466 expect(data).to.have.lengthOf(2)
467 expect(data[0].displayName).to.equal('playlist 3')
468 expect(data[1].displayName).to.equal('playlist 2')
469 }
470 }
471 })
472
473 it('Should not get unlisted playlist using only the id', async function () {
474 await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
475 })
476
477 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
478 await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid })
479 await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID })
480 })
481
482 it('Should not get private playlist without token', async function () {
483 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
484 await servers[1].playlists.get({ playlistId: id, expectedStatus: 401 })
485 }
486 })
487
488 it('Should get private playlist with a token', async function () {
489 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
490 await servers[1].playlists.get({ token: servers[1].accessToken, playlistId: id })
491 }
492 })
493 })
494
495 describe('Update playlists', function () {
496
497 it('Should update a playlist', async function () {
498 this.timeout(30000)
499
500 await servers[1].playlists.update({
501 attributes: {
502 displayName: 'playlist 3 updated',
503 description: 'description updated',
504 privacy: VideoPlaylistPrivacy.UNLISTED,
505 thumbnailfile: 'thumbnail.jpg',
506 videoChannelId: servers[1].store.channel.id
507 },
508 playlistId: playlistServer2Id2
509 })
510
511 await waitJobs(servers)
512
513 for (const server of servers) {
514 const playlist = await server.playlists.get({ playlistId: playlistServer2UUID2 })
515
516 expect(playlist.displayName).to.equal('playlist 3 updated')
517 expect(playlist.description).to.equal('description updated')
518
519 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
520 expect(playlist.privacy.label).to.equal('Unlisted')
521
522 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
523 expect(playlist.type.label).to.equal('Regular')
524
525 expect(playlist.videosLength).to.equal(2)
526
527 expect(playlist.ownerAccount.name).to.equal('root')
528 expect(playlist.ownerAccount.displayName).to.equal('root')
529 expect(playlist.videoChannel.name).to.equal('root_channel')
530 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
531 }
532 })
533 })
534
535 describe('Element timestamps', function () {
536
537 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
538 this.timeout(30000)
539
540 const addVideo = (attributes: any) => {
541 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
542 }
543
544 const playlistDisplayName = 'playlist 4'
545 const playlist = await commands[0].create({
546 attributes: {
547 displayName: playlistDisplayName,
548 privacy: VideoPlaylistPrivacy.PUBLIC,
549 videoChannelId: servers[0].store.channel.id
550 }
551 })
552
553 playlistServer1Id = playlist.id
554 playlistServer1DisplayName = playlistDisplayName
555 playlistServer1UUID = playlist.uuid
556
557 await addVideo({ videoId: servers[0].store.videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
558 await addVideo({ videoId: servers[2].store.videos[1].uuid, startTimestamp: 35 })
559 await addVideo({ videoId: servers[2].store.videos[2].uuid })
560 {
561 const element = await addVideo({ videoId: servers[0].store.videos[3].uuid, stopTimestamp: 35 })
562 playlistElementServer1Video4 = element.id
563 }
564
565 {
566 const element = await addVideo({ videoId: servers[0].store.videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
567 playlistElementServer1Video5 = element.id
568 }
569
570 {
571 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
572 playlistElementNSFW = element.id
573
574 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
575 await addVideo({ videoId: nsfwVideoServer1 })
576 }
577
578 await waitJobs(servers)
579 })
580
581 it('Should correctly list playlist videos', async function () {
582 this.timeout(30000)
583
584 for (const server of servers) {
585 {
586 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
587
588 expect(body.total).to.equal(8)
589
590 const videoElements = body.data
591 expect(videoElements).to.have.lengthOf(8)
592
593 expect(videoElements[0].video.name).to.equal('video 0 server 1')
594 expect(videoElements[0].position).to.equal(1)
595 expect(videoElements[0].startTimestamp).to.equal(15)
596 expect(videoElements[0].stopTimestamp).to.equal(28)
597
598 expect(videoElements[1].video.name).to.equal('video 1 server 3')
599 expect(videoElements[1].position).to.equal(2)
600 expect(videoElements[1].startTimestamp).to.equal(35)
601 expect(videoElements[1].stopTimestamp).to.be.null
602
603 expect(videoElements[2].video.name).to.equal('video 2 server 3')
604 expect(videoElements[2].position).to.equal(3)
605 expect(videoElements[2].startTimestamp).to.be.null
606 expect(videoElements[2].stopTimestamp).to.be.null
607
608 expect(videoElements[3].video.name).to.equal('video 3 server 1')
609 expect(videoElements[3].position).to.equal(4)
610 expect(videoElements[3].startTimestamp).to.be.null
611 expect(videoElements[3].stopTimestamp).to.equal(35)
612
613 expect(videoElements[4].video.name).to.equal('video 4 server 1')
614 expect(videoElements[4].position).to.equal(5)
615 expect(videoElements[4].startTimestamp).to.equal(45)
616 expect(videoElements[4].stopTimestamp).to.equal(60)
617
618 expect(videoElements[5].video.name).to.equal('NSFW video')
619 expect(videoElements[5].position).to.equal(6)
620 expect(videoElements[5].startTimestamp).to.equal(5)
621 expect(videoElements[5].stopTimestamp).to.be.null
622
623 expect(videoElements[6].video.name).to.equal('NSFW video')
624 expect(videoElements[6].position).to.equal(7)
625 expect(videoElements[6].startTimestamp).to.equal(4)
626 expect(videoElements[6].stopTimestamp).to.be.null
627
628 expect(videoElements[7].video.name).to.equal('NSFW video')
629 expect(videoElements[7].position).to.equal(8)
630 expect(videoElements[7].startTimestamp).to.be.null
631 expect(videoElements[7].stopTimestamp).to.be.null
632 }
633
634 {
635 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
636 expect(body.data).to.have.lengthOf(2)
637 }
638 }
639 })
640 })
641
642 describe('Element type', function () {
643 let groupUser1: PeerTubeServer[]
644 let groupWithoutToken1: PeerTubeServer[]
645 let group1: PeerTubeServer[]
646 let group2: PeerTubeServer[]
647
648 let video1: string
649 let video2: string
650 let video3: string
651
652 before(async function () {
653 this.timeout(60000)
654
655 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
656 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
657 group1 = [ servers[0] ]
658 group2 = [ servers[1], servers[2] ]
659
660 const playlist = await commands[0].create({
661 token: userTokenServer1,
662 attributes: {
663 displayName: 'playlist 56',
664 privacy: VideoPlaylistPrivacy.PUBLIC,
665 videoChannelId: servers[0].store.channel.id
666 }
667 })
668
669 const playlistServer1Id2 = playlist.id
670 playlistServer1UUID2 = playlist.uuid
671
672 const addVideo = (attributes: any) => {
673 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
674 }
675
676 video1 = (await servers[0].videos.quickUpload({ name: 'video 89', token: userTokenServer1 })).uuid
677 video2 = (await servers[1].videos.quickUpload({ name: 'video 90' })).uuid
678 video3 = (await servers[0].videos.quickUpload({ name: 'video 91', nsfw: true })).uuid
679
680 await waitJobs(servers)
681
682 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
683 await addVideo({ videoId: video2, startTimestamp: 35 })
684 await addVideo({ videoId: video3 })
685
686 await waitJobs(servers)
687 })
688
689 it('Should update the element type if the video is private', async function () {
690 this.timeout(20000)
691
692 const name = 'video 89'
693 const position = 1
694
695 {
696 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PRIVATE } })
697 await waitJobs(servers)
698
699 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
700 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
701 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
702 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
703 }
704
705 {
706 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } })
707 await waitJobs(servers)
708
709 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
710 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
711 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
712 // We deleted the video, so even if we recreated it, the old entry is still deleted
713 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
714 }
715 })
716
717 it('Should update the element type if the video is blacklisted', async function () {
718 this.timeout(20000)
719
720 const name = 'video 89'
721 const position = 1
722
723 {
724 await servers[0].blacklist.add({ videoId: video1, reason: 'reason', unfederate: true })
725 await waitJobs(servers)
726
727 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
728 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
729 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
730 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
731 }
732
733 {
734 await servers[0].blacklist.remove({ videoId: video1 })
735 await waitJobs(servers)
736
737 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
738 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
739 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
740 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
741 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
742 }
743 })
744
745 it('Should update the element type if the account or server of the video is blocked', async function () {
746 this.timeout(90000)
747
748 const command = servers[0].blocklist
749
750 const name = 'video 90'
751 const position = 2
752
753 {
754 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@' + servers[1].host })
755 await waitJobs(servers)
756
757 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
758 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
759
760 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@' + servers[1].host })
761 await waitJobs(servers)
762
763 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
764 }
765
766 {
767 await command.addToMyBlocklist({ token: userTokenServer1, server: servers[1].host })
768 await waitJobs(servers)
769
770 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
771 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
772
773 await command.removeFromMyBlocklist({ token: userTokenServer1, server: servers[1].host })
774 await waitJobs(servers)
775
776 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
777 }
778
779 {
780 await command.addToServerBlocklist({ account: 'root@' + servers[1].host })
781 await waitJobs(servers)
782
783 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
784 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
785
786 await command.removeFromServerBlocklist({ account: 'root@' + servers[1].host })
787 await waitJobs(servers)
788
789 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
790 }
791
792 {
793 await command.addToServerBlocklist({ server: servers[1].host })
794 await waitJobs(servers)
795
796 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
797 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
798
799 await command.removeFromServerBlocklist({ server: servers[1].host })
800 await waitJobs(servers)
801
802 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
803 }
804 })
805 })
806
807 describe('Managing playlist elements', function () {
808
809 it('Should reorder the playlist', async function () {
810 this.timeout(30000)
811
812 {
813 await commands[0].reorderElements({
814 playlistId: playlistServer1Id,
815 attributes: {
816 startPosition: 2,
817 insertAfterPosition: 3
818 }
819 })
820
821 await waitJobs(servers)
822
823 for (const server of servers) {
824 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
825 const names = body.data.map(v => v.video.name)
826
827 expect(names).to.deep.equal([
828 'video 0 server 1',
829 'video 2 server 3',
830 'video 1 server 3',
831 'video 3 server 1',
832 'video 4 server 1',
833 'NSFW video',
834 'NSFW video',
835 'NSFW video'
836 ])
837 }
838 }
839
840 {
841 await commands[0].reorderElements({
842 playlistId: playlistServer1Id,
843 attributes: {
844 startPosition: 1,
845 reorderLength: 3,
846 insertAfterPosition: 4
847 }
848 })
849
850 await waitJobs(servers)
851
852 for (const server of servers) {
853 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
854 const names = body.data.map(v => v.video.name)
855
856 expect(names).to.deep.equal([
857 'video 3 server 1',
858 'video 0 server 1',
859 'video 2 server 3',
860 'video 1 server 3',
861 'video 4 server 1',
862 'NSFW video',
863 'NSFW video',
864 'NSFW video'
865 ])
866 }
867 }
868
869 {
870 await commands[0].reorderElements({
871 playlistId: playlistServer1Id,
872 attributes: {
873 startPosition: 6,
874 insertAfterPosition: 3
875 }
876 })
877
878 await waitJobs(servers)
879
880 for (const server of servers) {
881 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
882 const names = elements.map(v => v.video.name)
883
884 expect(names).to.deep.equal([
885 'video 3 server 1',
886 'video 0 server 1',
887 'video 2 server 3',
888 'NSFW video',
889 'video 1 server 3',
890 'video 4 server 1',
891 'NSFW video',
892 'NSFW video'
893 ])
894
895 for (let i = 1; i <= elements.length; i++) {
896 expect(elements[i - 1].position).to.equal(i)
897 }
898 }
899 }
900 })
901
902 it('Should update startTimestamp/endTimestamp of some elements', async function () {
903 this.timeout(30000)
904
905 await commands[0].updateElement({
906 playlistId: playlistServer1Id,
907 elementId: playlistElementServer1Video4,
908 attributes: {
909 startTimestamp: 1
910 }
911 })
912
913 await commands[0].updateElement({
914 playlistId: playlistServer1Id,
915 elementId: playlistElementServer1Video5,
916 attributes: {
917 stopTimestamp: null
918 }
919 })
920
921 await waitJobs(servers)
922
923 for (const server of servers) {
924 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
925
926 expect(elements[0].video.name).to.equal('video 3 server 1')
927 expect(elements[0].position).to.equal(1)
928 expect(elements[0].startTimestamp).to.equal(1)
929 expect(elements[0].stopTimestamp).to.equal(35)
930
931 expect(elements[5].video.name).to.equal('video 4 server 1')
932 expect(elements[5].position).to.equal(6)
933 expect(elements[5].startTimestamp).to.equal(45)
934 expect(elements[5].stopTimestamp).to.be.null
935 }
936 })
937
938 it('Should check videos existence in my playlist', async function () {
939 const videoIds = [
940 servers[0].store.videos[0].id,
941 42000,
942 servers[0].store.videos[3].id,
943 43000,
944 servers[0].store.videos[4].id
945 ]
946 const obj = await commands[0].videosExist({ videoIds })
947
948 {
949 const elem = obj[servers[0].store.videos[0].id]
950 expect(elem).to.have.lengthOf(1)
951 expect(elem[0].playlistElementId).to.exist
952 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
953 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
954 expect(elem[0].playlistId).to.equal(playlistServer1Id)
955 expect(elem[0].startTimestamp).to.equal(15)
956 expect(elem[0].stopTimestamp).to.equal(28)
957 }
958
959 {
960 const elem = obj[servers[0].store.videos[3].id]
961 expect(elem).to.have.lengthOf(1)
962 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
963 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
964 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
965 expect(elem[0].playlistId).to.equal(playlistServer1Id)
966 expect(elem[0].startTimestamp).to.equal(1)
967 expect(elem[0].stopTimestamp).to.equal(35)
968 }
969
970 {
971 const elem = obj[servers[0].store.videos[4].id]
972 expect(elem).to.have.lengthOf(1)
973 expect(elem[0].playlistId).to.equal(playlistServer1Id)
974 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
975 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
976 expect(elem[0].startTimestamp).to.equal(45)
977 expect(elem[0].stopTimestamp).to.equal(null)
978 }
979
980 expect(obj[42000]).to.have.lengthOf(0)
981 expect(obj[43000]).to.have.lengthOf(0)
982 })
983
984 it('Should automatically update updatedAt field of playlists', async function () {
985 const server = servers[1]
986 const videoId = servers[1].store.videos[5].id
987
988 async function getPlaylistNames () {
989 const { data } = await server.playlists.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
990
991 return data.map(p => p.displayName)
992 }
993
994 const attributes = { videoId }
995 const element1 = await server.playlists.addElement({ playlistId: playlistServer2Id1, attributes })
996 const element2 = await server.playlists.addElement({ playlistId: playlistServer2Id2, attributes })
997
998 const names1 = await getPlaylistNames()
999 expect(names1[0]).to.equal('playlist 3 updated')
1000 expect(names1[1]).to.equal('playlist 2')
1001
1002 await server.playlists.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
1003
1004 const names2 = await getPlaylistNames()
1005 expect(names2[0]).to.equal('playlist 2')
1006 expect(names2[1]).to.equal('playlist 3 updated')
1007
1008 await server.playlists.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
1009
1010 const names3 = await getPlaylistNames()
1011 expect(names3[0]).to.equal('playlist 3 updated')
1012 expect(names3[1]).to.equal('playlist 2')
1013 })
1014
1015 it('Should delete some elements', async function () {
1016 this.timeout(30000)
1017
1018 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
1019 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
1020
1021 await waitJobs(servers)
1022
1023 for (const server of servers) {
1024 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
1025 expect(body.total).to.equal(6)
1026
1027 const elements = body.data
1028 expect(elements).to.have.lengthOf(6)
1029
1030 expect(elements[0].video.name).to.equal('video 0 server 1')
1031 expect(elements[0].position).to.equal(1)
1032
1033 expect(elements[1].video.name).to.equal('video 2 server 3')
1034 expect(elements[1].position).to.equal(2)
1035
1036 expect(elements[2].video.name).to.equal('video 1 server 3')
1037 expect(elements[2].position).to.equal(3)
1038
1039 expect(elements[3].video.name).to.equal('video 4 server 1')
1040 expect(elements[3].position).to.equal(4)
1041
1042 expect(elements[4].video.name).to.equal('NSFW video')
1043 expect(elements[4].position).to.equal(5)
1044
1045 expect(elements[5].video.name).to.equal('NSFW video')
1046 expect(elements[5].position).to.equal(6)
1047 }
1048 })
1049
1050 it('Should be able to create a public playlist, and set it to private', async function () {
1051 this.timeout(30000)
1052
1053 const videoPlaylistIds = await commands[0].create({
1054 attributes: {
1055 displayName: 'my super public playlist',
1056 privacy: VideoPlaylistPrivacy.PUBLIC,
1057 videoChannelId: servers[0].store.channel.id
1058 }
1059 })
1060
1061 await waitJobs(servers)
1062
1063 for (const server of servers) {
1064 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1065 }
1066
1067 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1068 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1069
1070 await waitJobs(servers)
1071
1072 for (const server of [ servers[1], servers[2] ]) {
1073 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1074 }
1075
1076 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1077 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1078 })
1079 })
1080
1081 describe('Playlist deletion', function () {
1082
1083 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1084 this.timeout(30000)
1085
1086 await commands[0].delete({ playlistId: playlistServer1Id })
1087
1088 await waitJobs(servers)
1089
1090 for (const server of servers) {
1091 await server.playlists.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1092 }
1093 })
1094
1095 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1096 this.timeout(30000)
1097
1098 for (const server of servers) {
1099 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server)
1100 }
1101 })
1102
1103 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1104 this.timeout(30000)
1105
1106 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1107
1108 {
1109 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1110 expect(body.total).to.equal(3)
1111
1112 expect(finder(body.data)).to.not.be.undefined
1113 }
1114
1115 await servers[2].follows.unfollow({ target: servers[0] })
1116
1117 {
1118 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1119 expect(body.total).to.equal(1)
1120
1121 expect(finder(body.data)).to.be.undefined
1122 }
1123 })
1124
1125 it('Should delete a channel and put the associated playlist in private mode', async function () {
1126 this.timeout(30000)
1127
1128 const channel = await servers[0].channels.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1129
1130 const playlistCreated = await commands[0].create({
1131 attributes: {
1132 displayName: 'channel playlist',
1133 privacy: VideoPlaylistPrivacy.PUBLIC,
1134 videoChannelId: channel.id
1135 }
1136 })
1137
1138 await waitJobs(servers)
1139
1140 await servers[0].channels.delete({ channelName: 'super_channel' })
1141
1142 await waitJobs(servers)
1143
1144 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1145 expect(body.displayName).to.equal('channel playlist')
1146 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1147
1148 await servers[1].playlists.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1149 })
1150
1151 it('Should delete an account and delete its playlists', async function () {
1152 this.timeout(30000)
1153
1154 const { userId, token } = await servers[0].users.generate('user_1')
1155
1156 const { videoChannels } = await servers[0].users.getMyInfo({ token })
1157 const userChannel = videoChannels[0]
1158
1159 await commands[0].create({
1160 attributes: {
1161 displayName: 'playlist to be deleted',
1162 privacy: VideoPlaylistPrivacy.PUBLIC,
1163 videoChannelId: userChannel.id
1164 }
1165 })
1166
1167 await waitJobs(servers)
1168
1169 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1170
1171 {
1172 for (const server of [ servers[0], servers[1] ]) {
1173 const body = await server.playlists.list({ start: 0, count: 15 })
1174
1175 expect(finder(body.data)).to.not.be.undefined
1176 }
1177 }
1178
1179 await servers[0].users.remove({ userId })
1180 await waitJobs(servers)
1181
1182 {
1183 for (const server of [ servers[0], servers[1] ]) {
1184 const body = await server.playlists.list({ start: 0, count: 15 })
1185
1186 expect(finder(body.data)).to.be.undefined
1187 }
1188 }
1189 })
1190 })
1191
1192 after(async function () {
1193 await cleanupTests(servers)
1194 })
1195 })