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