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