Fix playlist get for classic users
[oweals/peertube.git] / server / tests / api / videos / video-playlists.ts
1 /* tslint:disable:no-unused-expression */
2
3 import * as chai from 'chai'
4 import 'mocha'
5 import {
6   addVideoChannel,
7   addVideoInPlaylist,
8   checkPlaylistFilesWereRemoved,
9   cleanupTests,
10   createUser,
11   createVideoPlaylist,
12   deleteVideoChannel,
13   deleteVideoPlaylist,
14   doubleFollow,
15   doVideosExistInMyPlaylist,
16   flushAndRunMultipleServers,
17   getAccountPlaylistsList,
18   getAccountPlaylistsListWithToken,
19   getMyUserInformation,
20   getPlaylistVideos,
21   getVideoChannelPlaylistsList,
22   getVideoPlaylist,
23   getVideoPlaylistPrivacies,
24   getVideoPlaylistsList,
25   getVideoPlaylistWithToken,
26   removeUser,
27   removeVideoFromPlaylist,
28   reorderVideosPlaylist,
29   ServerInfo,
30   setAccessTokensToServers,
31   setDefaultVideoChannel,
32   testImage,
33   unfollow,
34   updateVideoPlaylist,
35   updateVideoPlaylistElement,
36   uploadVideo,
37   uploadVideoAndGetId,
38   userLogin,
39   waitJobs,
40   generateUserAccessToken
41 } from '../../../../shared/extra-utils'
42 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
43 import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
44 import { Video } from '../../../../shared/models/videos'
45 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
46 import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
47 import { User } from '../../../../shared/models/users'
48
49 const expect = chai.expect
50
51 describe('Test video playlists', function () {
52   let servers: ServerInfo[] = []
53
54   let playlistServer2Id1: number
55   let playlistServer2Id2: number
56   let playlistServer2UUID2: number
57
58   let playlistServer1Id: number
59   let playlistServer1UUID: string
60
61   let nsfwVideoServer1: number
62
63   before(async function () {
64     this.timeout(120000)
65
66     servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
67
68     // Get the access tokens
69     await setAccessTokensToServers(servers)
70     await setDefaultVideoChannel(servers)
71
72     // Server 1 and server 2 follow each other
73     await doubleFollow(servers[0], servers[1])
74     // Server 1 and server 3 follow each other
75     await doubleFollow(servers[0], servers[2])
76
77     {
78       const serverPromises: Promise<any>[][] = []
79
80       for (const server of servers) {
81         const videoPromises: Promise<any>[] = []
82
83         for (let i = 0; i < 7; i++) {
84           videoPromises.push(
85             uploadVideo(server.url, server.accessToken, { name: `video ${i} server ${server.serverNumber}`, nsfw: false })
86               .then(res => res.body.video)
87           )
88         }
89
90         serverPromises.push(videoPromises)
91       }
92
93       servers[0].videos = await Promise.all(serverPromises[0])
94       servers[1].videos = await Promise.all(serverPromises[1])
95       servers[2].videos = await Promise.all(serverPromises[2])
96     }
97
98     nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[ 0 ], videoName: 'NSFW video', nsfw: true })).id
99
100     await waitJobs(servers)
101   })
102
103   it('Should list video playlist privacies', async function () {
104     const res = await getVideoPlaylistPrivacies(servers[0].url)
105
106     const privacies = res.body
107     expect(Object.keys(privacies)).to.have.length.at.least(3)
108
109     expect(privacies[3]).to.equal('Private')
110   })
111
112   it('Should list watch later playlist', async function () {
113     const url = servers[ 0 ].url
114     const accessToken = servers[ 0 ].accessToken
115
116     {
117       const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
118
119       expect(res.body.total).to.equal(1)
120       expect(res.body.data).to.have.lengthOf(1)
121
122       const playlist: VideoPlaylist = res.body.data[ 0 ]
123       expect(playlist.displayName).to.equal('Watch later')
124       expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
125       expect(playlist.type.label).to.equal('Watch later')
126     }
127
128     {
129       const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
130
131       expect(res.body.total).to.equal(0)
132       expect(res.body.data).to.have.lengthOf(0)
133     }
134
135     {
136       const res = await getAccountPlaylistsList(url, 'root', 0, 5)
137       expect(res.body.total).to.equal(0)
138       expect(res.body.data).to.have.lengthOf(0)
139     }
140   })
141
142   it('Should get private playlist for a classic user', async function () {
143     const token = await generateUserAccessToken(servers[0], 'toto')
144
145     const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
146
147     expect(res.body.total).to.equal(1)
148     expect(res.body.data).to.have.lengthOf(1)
149
150     const playlistId = res.body.data[0].id
151     await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
152   })
153
154   it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
155     this.timeout(30000)
156
157     await createVideoPlaylist({
158       url: servers[0].url,
159       token: servers[0].accessToken,
160       playlistAttrs: {
161         displayName: 'my super playlist',
162         privacy: VideoPlaylistPrivacy.PUBLIC,
163         description: 'my super description',
164         thumbnailfile: 'thumbnail.jpg',
165         videoChannelId: servers[0].videoChannel.id
166       }
167     })
168
169     await waitJobs(servers)
170
171     for (const server of servers) {
172       const res = await getVideoPlaylistsList(server.url, 0, 5)
173       expect(res.body.total).to.equal(1)
174       expect(res.body.data).to.have.lengthOf(1)
175
176       const playlistFromList = res.body.data[0] as VideoPlaylist
177
178       const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
179       const playlistFromGet = res2.body
180
181       for (const playlist of [ playlistFromGet, playlistFromList ]) {
182         expect(playlist.id).to.be.a('number')
183         expect(playlist.uuid).to.be.a('string')
184
185         expect(playlist.isLocal).to.equal(server.serverNumber === 1)
186
187         expect(playlist.displayName).to.equal('my super playlist')
188         expect(playlist.description).to.equal('my super description')
189         expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
190         expect(playlist.privacy.label).to.equal('Public')
191         expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
192         expect(playlist.type.label).to.equal('Regular')
193
194         expect(playlist.videosLength).to.equal(0)
195
196         expect(playlist.ownerAccount.name).to.equal('root')
197         expect(playlist.ownerAccount.displayName).to.equal('root')
198         expect(playlist.videoChannel.name).to.equal('root_channel')
199         expect(playlist.videoChannel.displayName).to.equal('Main root channel')
200       }
201     }
202   })
203
204   it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
205     this.timeout(30000)
206
207     {
208       const res = await createVideoPlaylist({
209         url: servers[1].url,
210         token: servers[1].accessToken,
211         playlistAttrs: {
212           displayName: 'playlist 2',
213           privacy: VideoPlaylistPrivacy.PUBLIC,
214           videoChannelId: servers[1].videoChannel.id
215         }
216       })
217       playlistServer2Id1 = res.body.videoPlaylist.id
218     }
219
220     {
221       const res = await createVideoPlaylist({
222         url: servers[ 1 ].url,
223         token: servers[ 1 ].accessToken,
224         playlistAttrs: {
225           displayName: 'playlist 3',
226           privacy: VideoPlaylistPrivacy.PUBLIC,
227           thumbnailfile: 'thumbnail.jpg',
228           videoChannelId: servers[1].videoChannel.id
229         }
230       })
231
232       playlistServer2Id2 = res.body.videoPlaylist.id
233       playlistServer2UUID2 = res.body.videoPlaylist.uuid
234     }
235
236     for (let id of [ playlistServer2Id1, playlistServer2Id2 ]) {
237       await addVideoInPlaylist({
238         url: servers[ 1 ].url,
239         token: servers[ 1 ].accessToken,
240         playlistId: id,
241         elementAttrs: { videoId: servers[ 1 ].videos[ 0 ].id, startTimestamp: 1, stopTimestamp: 2 }
242       })
243       await addVideoInPlaylist({
244         url: servers[ 1 ].url,
245         token: servers[ 1 ].accessToken,
246         playlistId: id,
247         elementAttrs: { videoId: servers[ 1 ].videos[ 1 ].id }
248       })
249     }
250
251     await waitJobs(servers)
252
253     for (const server of [ servers[0], servers[1] ]) {
254       const res = await getVideoPlaylistsList(server.url, 0, 5)
255
256       const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
257       expect(playlist2).to.not.be.undefined
258       await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
259
260       const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
261       expect(playlist3).to.not.be.undefined
262       await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
263     }
264
265     const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
266     expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
267     expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
268   })
269
270   it('Should have the playlist on server 3 after a new follow', async function () {
271     this.timeout(30000)
272
273     // Server 2 and server 3 follow each other
274     await doubleFollow(servers[1], servers[2])
275
276     const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
277
278     const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
279     expect(playlist2).to.not.be.undefined
280     await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
281
282     expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
283   })
284
285   it('Should correctly list the playlists', async function () {
286     this.timeout(30000)
287
288     {
289       const res = await getVideoPlaylistsList(servers[ 2 ].url, 1, 2, 'createdAt')
290
291       expect(res.body.total).to.equal(3)
292
293       const data: VideoPlaylist[] = res.body.data
294       expect(data).to.have.lengthOf(2)
295       expect(data[ 0 ].displayName).to.equal('playlist 2')
296       expect(data[ 1 ].displayName).to.equal('playlist 3')
297     }
298
299     {
300       const res = await getVideoPlaylistsList(servers[ 2 ].url, 1, 2, '-createdAt')
301
302       expect(res.body.total).to.equal(3)
303
304       const data: VideoPlaylist[] = res.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('my super playlist')
308     }
309   })
310
311   it('Should list video channel playlists', async function () {
312     this.timeout(30000)
313
314     {
315       const res = await getVideoChannelPlaylistsList(servers[ 0 ].url, 'root_channel', 0, 2, '-createdAt')
316
317       expect(res.body.total).to.equal(1)
318
319       const data: VideoPlaylist[] = res.body.data
320       expect(data).to.have.lengthOf(1)
321       expect(data[ 0 ].displayName).to.equal('my super playlist')
322     }
323   })
324
325   it('Should list account playlists', async function () {
326     this.timeout(30000)
327
328     {
329       const res = await getAccountPlaylistsList(servers[ 1 ].url, 'root', 1, 2, '-createdAt')
330
331       expect(res.body.total).to.equal(2)
332
333       const data: VideoPlaylist[] = res.body.data
334       expect(data).to.have.lengthOf(1)
335       expect(data[ 0 ].displayName).to.equal('playlist 2')
336     }
337
338     {
339       const res = await getAccountPlaylistsList(servers[ 1 ].url, 'root', 1, 2, 'createdAt')
340
341       expect(res.body.total).to.equal(2)
342
343       const data: VideoPlaylist[] = res.body.data
344       expect(data).to.have.lengthOf(1)
345       expect(data[ 0 ].displayName).to.equal('playlist 3')
346     }
347   })
348
349   it('Should not list unlisted or private playlists', async function () {
350     this.timeout(30000)
351
352     await createVideoPlaylist({
353       url: servers[ 1 ].url,
354       token: servers[ 1 ].accessToken,
355       playlistAttrs: {
356         displayName: 'playlist unlisted',
357         privacy: VideoPlaylistPrivacy.UNLISTED
358       }
359     })
360
361     await createVideoPlaylist({
362       url: servers[ 1 ].url,
363       token: servers[ 1 ].accessToken,
364       playlistAttrs: {
365         displayName: 'playlist private',
366         privacy: VideoPlaylistPrivacy.PRIVATE
367       }
368     })
369
370     await waitJobs(servers)
371
372     for (const server of servers) {
373       const results = [
374         await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
375         await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
376       ]
377
378       expect(results[0].body.total).to.equal(2)
379       expect(results[1].body.total).to.equal(3)
380
381       for (const res of results) {
382         const data: VideoPlaylist[] = res.body.data
383         expect(data).to.have.lengthOf(2)
384         expect(data[ 0 ].displayName).to.equal('playlist 3')
385         expect(data[ 1 ].displayName).to.equal('playlist 2')
386       }
387     }
388   })
389
390   it('Should update a playlist', async function () {
391     this.timeout(30000)
392
393     await updateVideoPlaylist({
394       url: servers[1].url,
395       token: servers[1].accessToken,
396       playlistAttrs: {
397         displayName: 'playlist 3 updated',
398         description: 'description updated',
399         privacy: VideoPlaylistPrivacy.UNLISTED,
400         thumbnailfile: 'thumbnail.jpg',
401         videoChannelId: servers[1].videoChannel.id
402       },
403       playlistId: playlistServer2Id2
404     })
405
406     await waitJobs(servers)
407
408     for (const server of servers) {
409       const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
410       const playlist: VideoPlaylist = res.body
411
412       expect(playlist.displayName).to.equal('playlist 3 updated')
413       expect(playlist.description).to.equal('description updated')
414
415       expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
416       expect(playlist.privacy.label).to.equal('Unlisted')
417
418       expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
419       expect(playlist.type.label).to.equal('Regular')
420
421       expect(playlist.videosLength).to.equal(2)
422
423       expect(playlist.ownerAccount.name).to.equal('root')
424       expect(playlist.ownerAccount.displayName).to.equal('root')
425       expect(playlist.videoChannel.name).to.equal('root_channel')
426       expect(playlist.videoChannel.displayName).to.equal('Main root channel')
427     }
428   })
429
430   it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
431     this.timeout(30000)
432
433     const addVideo = (elementAttrs: any) => {
434       return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
435     }
436
437     const res = await createVideoPlaylist({
438       url: servers[ 0 ].url,
439       token: servers[ 0 ].accessToken,
440       playlistAttrs: {
441         displayName: 'playlist 4',
442         privacy: VideoPlaylistPrivacy.PUBLIC,
443         videoChannelId: servers[0].videoChannel.id
444       }
445     })
446
447     playlistServer1Id = res.body.videoPlaylist.id
448     playlistServer1UUID = res.body.videoPlaylist.uuid
449
450     await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
451     await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
452     await addVideo({ videoId: servers[2].videos[2].uuid })
453     await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
454     await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
455     await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
456
457     await waitJobs(servers)
458   })
459
460   it('Should correctly list playlist videos', async function () {
461     this.timeout(30000)
462
463     for (const server of servers) {
464       const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
465
466       expect(res.body.total).to.equal(6)
467
468       const videos: Video[] = res.body.data
469       expect(videos).to.have.lengthOf(6)
470
471       expect(videos[0].name).to.equal('video 0 server 1')
472       expect(videos[0].playlistElement.position).to.equal(1)
473       expect(videos[0].playlistElement.startTimestamp).to.equal(15)
474       expect(videos[0].playlistElement.stopTimestamp).to.equal(28)
475
476       expect(videos[1].name).to.equal('video 1 server 3')
477       expect(videos[1].playlistElement.position).to.equal(2)
478       expect(videos[1].playlistElement.startTimestamp).to.equal(35)
479       expect(videos[1].playlistElement.stopTimestamp).to.be.null
480
481       expect(videos[2].name).to.equal('video 2 server 3')
482       expect(videos[2].playlistElement.position).to.equal(3)
483       expect(videos[2].playlistElement.startTimestamp).to.be.null
484       expect(videos[2].playlistElement.stopTimestamp).to.be.null
485
486       expect(videos[3].name).to.equal('video 3 server 1')
487       expect(videos[3].playlistElement.position).to.equal(4)
488       expect(videos[3].playlistElement.startTimestamp).to.be.null
489       expect(videos[3].playlistElement.stopTimestamp).to.equal(35)
490
491       expect(videos[4].name).to.equal('video 4 server 1')
492       expect(videos[4].playlistElement.position).to.equal(5)
493       expect(videos[4].playlistElement.startTimestamp).to.equal(45)
494       expect(videos[4].playlistElement.stopTimestamp).to.equal(60)
495
496       expect(videos[5].name).to.equal('NSFW video')
497       expect(videos[5].playlistElement.position).to.equal(6)
498       expect(videos[5].playlistElement.startTimestamp).to.equal(5)
499       expect(videos[5].playlistElement.stopTimestamp).to.be.null
500
501       const res2 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10, { nsfw: false })
502       expect(res2.body.total).to.equal(5)
503       expect(res2.body.data.find(v => v.name === 'NSFW video')).to.be.undefined
504
505       const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
506       expect(res3.body.data).to.have.lengthOf(2)
507     }
508   })
509
510   it('Should reorder the playlist', async function () {
511     this.timeout(30000)
512
513     {
514       await reorderVideosPlaylist({
515         url: servers[ 0 ].url,
516         token: servers[ 0 ].accessToken,
517         playlistId: playlistServer1Id,
518         elementAttrs: {
519           startPosition: 2,
520           insertAfterPosition: 3
521         }
522       })
523
524       await waitJobs(servers)
525
526       for (const server of servers) {
527         const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
528         const names = res.body.data.map(v => v.name)
529
530         expect(names).to.deep.equal([
531           'video 0 server 1',
532           'video 2 server 3',
533           'video 1 server 3',
534           'video 3 server 1',
535           'video 4 server 1',
536           'NSFW video'
537         ])
538       }
539     }
540
541     {
542       await reorderVideosPlaylist({
543         url: servers[0].url,
544         token: servers[0].accessToken,
545         playlistId: playlistServer1Id,
546         elementAttrs: {
547           startPosition: 1,
548           reorderLength: 3,
549           insertAfterPosition: 4
550         }
551       })
552
553       await waitJobs(servers)
554
555       for (const server of servers) {
556         const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
557         const names = res.body.data.map(v => v.name)
558
559         expect(names).to.deep.equal([
560           'video 3 server 1',
561           'video 0 server 1',
562           'video 2 server 3',
563           'video 1 server 3',
564           'video 4 server 1',
565           'NSFW video'
566         ])
567       }
568     }
569
570     {
571       await reorderVideosPlaylist({
572         url: servers[0].url,
573         token: servers[0].accessToken,
574         playlistId: playlistServer1Id,
575         elementAttrs: {
576           startPosition: 6,
577           insertAfterPosition: 3
578         }
579       })
580
581       await waitJobs(servers)
582
583       for (const server of servers) {
584         const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
585         const videos: Video[] = res.body.data
586
587         const names = videos.map(v => v.name)
588
589         expect(names).to.deep.equal([
590           'video 3 server 1',
591           'video 0 server 1',
592           'video 2 server 3',
593           'NSFW video',
594           'video 1 server 3',
595           'video 4 server 1'
596         ])
597
598         for (let i = 1; i <= videos.length; i++) {
599           expect(videos[i - 1].playlistElement.position).to.equal(i)
600         }
601       }
602     }
603   })
604
605   it('Should update startTimestamp/endTimestamp of some elements', async function () {
606     this.timeout(30000)
607
608     await updateVideoPlaylistElement({
609       url: servers[0].url,
610       token: servers[0].accessToken,
611       playlistId: playlistServer1Id,
612       videoId: servers[0].videos[3].uuid,
613       elementAttrs: {
614         startTimestamp: 1
615       }
616     })
617
618     await updateVideoPlaylistElement({
619       url: servers[0].url,
620       token: servers[0].accessToken,
621       playlistId: playlistServer1Id,
622       videoId: servers[0].videos[4].uuid,
623       elementAttrs: {
624         stopTimestamp: null
625       }
626     })
627
628     await waitJobs(servers)
629
630     for (const server of servers) {
631       const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
632       const videos: Video[] = res.body.data
633
634       expect(videos[0].name).to.equal('video 3 server 1')
635       expect(videos[0].playlistElement.position).to.equal(1)
636       expect(videos[0].playlistElement.startTimestamp).to.equal(1)
637       expect(videos[0].playlistElement.stopTimestamp).to.equal(35)
638
639       expect(videos[5].name).to.equal('video 4 server 1')
640       expect(videos[5].playlistElement.position).to.equal(6)
641       expect(videos[5].playlistElement.startTimestamp).to.equal(45)
642       expect(videos[5].playlistElement.stopTimestamp).to.be.null
643     }
644   })
645
646   it('Should check videos existence in my playlist', async function () {
647     const videoIds = [
648       servers[0].videos[0].id,
649       42000,
650       servers[0].videos[3].id,
651       43000,
652       servers[0].videos[4].id
653     ]
654     const res = await doVideosExistInMyPlaylist(servers[ 0 ].url, servers[ 0 ].accessToken, videoIds)
655     const obj = res.body as VideoExistInPlaylist
656
657     {
658       const elem = obj[servers[0].videos[0].id]
659       expect(elem).to.have.lengthOf(1)
660       expect(elem[ 0 ].playlistId).to.equal(playlistServer1Id)
661       expect(elem[ 0 ].startTimestamp).to.equal(15)
662       expect(elem[ 0 ].stopTimestamp).to.equal(28)
663     }
664
665     {
666       const elem = obj[servers[0].videos[3].id]
667       expect(elem).to.have.lengthOf(1)
668       expect(elem[ 0 ].playlistId).to.equal(playlistServer1Id)
669       expect(elem[ 0 ].startTimestamp).to.equal(1)
670       expect(elem[ 0 ].stopTimestamp).to.equal(35)
671     }
672
673     {
674       const elem = obj[servers[0].videos[4].id]
675       expect(elem).to.have.lengthOf(1)
676       expect(elem[ 0 ].playlistId).to.equal(playlistServer1Id)
677       expect(elem[ 0 ].startTimestamp).to.equal(45)
678       expect(elem[ 0 ].stopTimestamp).to.equal(null)
679     }
680
681     expect(obj[42000]).to.have.lengthOf(0)
682     expect(obj[43000]).to.have.lengthOf(0)
683   })
684
685   it('Should automatically update updatedAt field of playlists', async function () {
686     const server = servers[1]
687     const videoId = servers[1].videos[5].id
688
689     async function getPlaylistNames () {
690       const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
691
692       return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
693     }
694
695     const elementAttrs = { videoId }
696     await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
697     await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
698
699     const names1 = await getPlaylistNames()
700     expect(names1[0]).to.equal('playlist 3 updated')
701     expect(names1[1]).to.equal('playlist 2')
702
703     await removeVideoFromPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, videoId })
704
705     const names2 = await getPlaylistNames()
706     expect(names2[0]).to.equal('playlist 2')
707     expect(names2[1]).to.equal('playlist 3 updated')
708
709     await removeVideoFromPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, videoId })
710
711     const names3 = await getPlaylistNames()
712     expect(names3[0]).to.equal('playlist 3 updated')
713     expect(names3[1]).to.equal('playlist 2')
714   })
715
716   it('Should delete some elements', async function () {
717     this.timeout(30000)
718
719     await removeVideoFromPlaylist({
720       url: servers[0].url,
721       token: servers[0].accessToken,
722       playlistId: playlistServer1Id,
723       videoId: servers[0].videos[3].uuid
724     })
725
726     await removeVideoFromPlaylist({
727       url: servers[0].url,
728       token: servers[0].accessToken,
729       playlistId: playlistServer1Id,
730       videoId: nsfwVideoServer1
731     })
732
733     await waitJobs(servers)
734
735     for (const server of servers) {
736       const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
737
738       expect(res.body.total).to.equal(4)
739
740       const videos: Video[] = res.body.data
741       expect(videos).to.have.lengthOf(4)
742
743       expect(videos[ 0 ].name).to.equal('video 0 server 1')
744       expect(videos[ 0 ].playlistElement.position).to.equal(1)
745
746       expect(videos[ 1 ].name).to.equal('video 2 server 3')
747       expect(videos[ 1 ].playlistElement.position).to.equal(2)
748
749       expect(videos[ 2 ].name).to.equal('video 1 server 3')
750       expect(videos[ 2 ].playlistElement.position).to.equal(3)
751
752       expect(videos[ 3 ].name).to.equal('video 4 server 1')
753       expect(videos[ 3 ].playlistElement.position).to.equal(4)
754     }
755   })
756
757   it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
758     this.timeout(30000)
759
760     await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
761
762     await waitJobs(servers)
763
764     for (const server of servers) {
765       await getVideoPlaylist(server.url, playlistServer1UUID, 404)
766     }
767   })
768
769   it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
770     this.timeout(30000)
771
772     for (const server of servers) {
773       await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
774     }
775   })
776
777   it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
778     this.timeout(30000)
779
780     const finder = data => data.find(p => p.displayName === 'my super playlist')
781
782     {
783       const res = await getVideoPlaylistsList(servers[ 2 ].url, 0, 5)
784       expect(res.body.total).to.equal(2)
785       expect(finder(res.body.data)).to.not.be.undefined
786     }
787
788     await unfollow(servers[2].url, servers[2].accessToken, servers[0])
789
790     {
791       const res = await getVideoPlaylistsList(servers[ 2 ].url, 0, 5)
792       expect(res.body.total).to.equal(1)
793
794       expect(finder(res.body.data)).to.be.undefined
795     }
796   })
797
798   it('Should delete a channel and put the associated playlist in private mode', async function () {
799     this.timeout(30000)
800
801     const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
802     const videoChannelId = res.body.videoChannel.id
803
804     const res2 = await createVideoPlaylist({
805       url: servers[0].url,
806       token: servers[0].accessToken,
807       playlistAttrs: {
808         displayName: 'channel playlist',
809         privacy: VideoPlaylistPrivacy.PUBLIC,
810         videoChannelId
811       }
812     })
813     const videoPlaylistUUID = res2.body.videoPlaylist.uuid
814
815     await waitJobs(servers)
816
817     await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
818
819     await waitJobs(servers)
820
821     const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
822     expect(res3.body.displayName).to.equal('channel playlist')
823     expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
824
825     await getVideoPlaylist(servers[1].url, videoPlaylistUUID, 404)
826   })
827
828   it('Should delete an account and delete its playlists', async function () {
829     this.timeout(30000)
830
831     const user = { username: 'user_1', password: 'password' }
832     const res = await createUser({
833       url: servers[ 0 ].url,
834       accessToken: servers[ 0 ].accessToken,
835       username: user.username,
836       password: user.password
837     })
838
839     const userId = res.body.user.id
840     const userAccessToken = await userLogin(servers[0], user)
841
842     const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
843     const userChannel = (resChannel.body as User).videoChannels[0]
844
845     await createVideoPlaylist({
846       url: servers[0].url,
847       token: userAccessToken,
848       playlistAttrs: {
849         displayName: 'playlist to be deleted',
850         privacy: VideoPlaylistPrivacy.PUBLIC,
851         videoChannelId: userChannel.id
852       }
853     })
854
855     await waitJobs(servers)
856
857     const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
858
859     {
860       for (const server of [ servers[0], servers[1] ]) {
861         const res = await getVideoPlaylistsList(server.url, 0, 15)
862         expect(finder(res.body.data)).to.not.be.undefined
863       }
864     }
865
866     await removeUser(servers[0].url, userId, servers[0].accessToken)
867     await waitJobs(servers)
868
869     {
870       for (const server of [ servers[0], servers[1] ]) {
871         const res = await getVideoPlaylistsList(server.url, 0, 15)
872         expect(finder(res.body.data)).to.be.undefined
873       }
874     }
875   })
876
877   after(async function () {
878     await cleanupTests(servers)
879   })
880 })