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