1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import * as chai from 'chai'
9 checkPlaylistFilesWereRemoved,
16 doVideosExistInMyPlaylist,
17 flushAndRunMultipleServers,
18 generateUserAccessToken,
20 getAccountPlaylistsList,
21 getAccountPlaylistsListWithToken,
24 getVideoChannelPlaylistsList,
26 getVideoPlaylistPrivacies,
27 getVideoPlaylistsList,
28 getVideoPlaylistWithToken,
30 removeVideoFromBlacklist,
31 removeVideoFromPlaylist,
32 reorderVideosPlaylist,
34 setAccessTokensToServers,
35 setDefaultVideoChannel,
40 updateVideoPlaylistElement,
45 } from '../../../../shared/extra-utils'
46 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
47 import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
48 import { VideoPrivacy } from '../../../../shared/models/videos'
49 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
50 import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
51 import { User } from '../../../../shared/models/users'
52 import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
54 addAccountToAccountBlocklist,
55 addAccountToServerBlocklist,
56 addServerToAccountBlocklist,
57 addServerToServerBlocklist,
58 removeAccountFromAccountBlocklist,
59 removeAccountFromServerBlocklist,
60 removeServerFromAccountBlocklist,
61 removeServerFromServerBlocklist
62 } from '../../../../shared/extra-utils/users/blocklist'
64 const expect = chai.expect
66 async function checkPlaylistElementType (
67 servers: ServerInfo[],
69 type: VideoPlaylistElementType,
74 for (const server of servers) {
75 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10)
76 expect(res.body.total).to.equal(total)
78 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position)
79 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
81 if (type === VideoPlaylistElementType.REGULAR) {
82 expect(videoElement.video).to.not.be.null
83 expect(videoElement.video.name).to.equal(name)
85 expect(videoElement.video).to.be.null
90 describe('Test video playlists', function () {
91 let servers: ServerInfo[] = []
93 let playlistServer2Id1: number
94 let playlistServer2Id2: number
95 let playlistServer2UUID2: number
97 let playlistServer1Id: number
98 let playlistServer1UUID: string
99 let playlistServer1UUID2: string
101 let playlistElementServer1Video4: number
102 let playlistElementServer1Video5: number
103 let playlistElementNSFW: number
105 let nsfwVideoServer1: number
107 let userAccessTokenServer1: string
109 before(async function () {
112 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
114 // Get the access tokens
115 await setAccessTokensToServers(servers)
116 await setDefaultVideoChannel(servers)
118 // Server 1 and server 2 follow each other
119 await doubleFollow(servers[0], servers[1])
120 // Server 1 and server 3 follow each other
121 await doubleFollow(servers[0], servers[2])
124 const serverPromises: Promise<any>[][] = []
126 for (const server of servers) {
127 const videoPromises: Promise<any>[] = []
129 for (let i = 0; i < 7; i++) {
131 uploadVideo(server.url, server.accessToken, { name: `video ${i} server ${server.serverNumber}`, nsfw: false })
132 .then(res => res.body.video)
136 serverPromises.push(videoPromises)
139 servers[0].videos = await Promise.all(serverPromises[0])
140 servers[1].videos = await Promise.all(serverPromises[1])
141 servers[2].videos = await Promise.all(serverPromises[2])
144 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
149 accessToken: servers[0].accessToken,
153 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
156 await waitJobs(servers)
159 describe('Get default playlists', function () {
160 it('Should list video playlist privacies', async function () {
161 const res = await getVideoPlaylistPrivacies(servers[0].url)
163 const privacies = res.body
164 expect(Object.keys(privacies)).to.have.length.at.least(3)
166 expect(privacies[3]).to.equal('Private')
169 it('Should list watch later playlist', async function () {
170 const url = servers[0].url
171 const accessToken = servers[0].accessToken
174 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
176 expect(res.body.total).to.equal(1)
177 expect(res.body.data).to.have.lengthOf(1)
179 const playlist: VideoPlaylist = res.body.data[0]
180 expect(playlist.displayName).to.equal('Watch later')
181 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
182 expect(playlist.type.label).to.equal('Watch later')
186 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
188 expect(res.body.total).to.equal(0)
189 expect(res.body.data).to.have.lengthOf(0)
193 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
194 expect(res.body.total).to.equal(0)
195 expect(res.body.data).to.have.lengthOf(0)
199 it('Should get private playlist for a classic user', async function () {
200 const token = await generateUserAccessToken(servers[0], 'toto')
202 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
204 expect(res.body.total).to.equal(1)
205 expect(res.body.data).to.have.lengthOf(1)
207 const playlistId = res.body.data[0].id
208 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
212 describe('Create and federate playlists', function () {
214 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
217 await createVideoPlaylist({
219 token: servers[0].accessToken,
221 displayName: 'my super playlist',
222 privacy: VideoPlaylistPrivacy.PUBLIC,
223 description: 'my super description',
224 thumbnailfile: 'thumbnail.jpg',
225 videoChannelId: servers[0].videoChannel.id
229 await waitJobs(servers)
231 for (const server of servers) {
232 const res = await getVideoPlaylistsList(server.url, 0, 5)
233 expect(res.body.total).to.equal(1)
234 expect(res.body.data).to.have.lengthOf(1)
236 const playlistFromList = res.body.data[0] as VideoPlaylist
238 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
239 const playlistFromGet = res2.body
241 for (const playlist of [ playlistFromGet, playlistFromList ]) {
242 expect(playlist.id).to.be.a('number')
243 expect(playlist.uuid).to.be.a('string')
245 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
247 expect(playlist.displayName).to.equal('my super playlist')
248 expect(playlist.description).to.equal('my super description')
249 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
250 expect(playlist.privacy.label).to.equal('Public')
251 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
252 expect(playlist.type.label).to.equal('Regular')
254 expect(playlist.videosLength).to.equal(0)
256 expect(playlist.ownerAccount.name).to.equal('root')
257 expect(playlist.ownerAccount.displayName).to.equal('root')
258 expect(playlist.videoChannel.name).to.equal('root_channel')
259 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
264 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
268 const res = await createVideoPlaylist({
270 token: servers[1].accessToken,
272 displayName: 'playlist 2',
273 privacy: VideoPlaylistPrivacy.PUBLIC,
274 videoChannelId: servers[1].videoChannel.id
277 playlistServer2Id1 = res.body.videoPlaylist.id
281 const res = await createVideoPlaylist({
283 token: servers[1].accessToken,
285 displayName: 'playlist 3',
286 privacy: VideoPlaylistPrivacy.PUBLIC,
287 thumbnailfile: 'thumbnail.jpg',
288 videoChannelId: servers[1].videoChannel.id
292 playlistServer2Id2 = res.body.videoPlaylist.id
293 playlistServer2UUID2 = res.body.videoPlaylist.uuid
296 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
297 await addVideoInPlaylist({
299 token: servers[1].accessToken,
301 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
303 await addVideoInPlaylist({
305 token: servers[1].accessToken,
307 elementAttrs: { videoId: servers[1].videos[1].id }
311 await waitJobs(servers)
313 for (const server of [ servers[0], servers[1] ]) {
314 const res = await getVideoPlaylistsList(server.url, 0, 5)
316 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
317 expect(playlist2).to.not.be.undefined
318 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
320 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
321 expect(playlist3).to.not.be.undefined
322 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
325 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
326 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
327 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
330 it('Should have the playlist on server 3 after a new follow', async function () {
333 // Server 2 and server 3 follow each other
334 await doubleFollow(servers[1], servers[2])
336 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
338 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
339 expect(playlist2).to.not.be.undefined
340 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
342 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
346 describe('List playlists', function () {
348 it('Should correctly list the playlists', async function () {
352 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt')
354 expect(res.body.total).to.equal(3)
356 const data: VideoPlaylist[] = res.body.data
357 expect(data).to.have.lengthOf(2)
358 expect(data[0].displayName).to.equal('playlist 2')
359 expect(data[1].displayName).to.equal('playlist 3')
363 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt')
365 expect(res.body.total).to.equal(3)
367 const data: VideoPlaylist[] = res.body.data
368 expect(data).to.have.lengthOf(2)
369 expect(data[0].displayName).to.equal('playlist 2')
370 expect(data[1].displayName).to.equal('my super playlist')
374 it('Should list video channel playlists', async function () {
378 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt')
380 expect(res.body.total).to.equal(1)
382 const data: VideoPlaylist[] = res.body.data
383 expect(data).to.have.lengthOf(1)
384 expect(data[0].displayName).to.equal('my super playlist')
388 it('Should list account playlists', async function () {
392 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt')
394 expect(res.body.total).to.equal(2)
396 const data: VideoPlaylist[] = res.body.data
397 expect(data).to.have.lengthOf(1)
398 expect(data[0].displayName).to.equal('playlist 2')
402 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt')
404 expect(res.body.total).to.equal(2)
406 const data: VideoPlaylist[] = res.body.data
407 expect(data).to.have.lengthOf(1)
408 expect(data[0].displayName).to.equal('playlist 3')
412 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3')
414 expect(res.body.total).to.equal(1)
416 const data: VideoPlaylist[] = res.body.data
417 expect(data).to.have.lengthOf(1)
418 expect(data[0].displayName).to.equal('playlist 3')
422 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4')
424 expect(res.body.total).to.equal(0)
426 const data: VideoPlaylist[] = res.body.data
427 expect(data).to.have.lengthOf(0)
431 it('Should not list unlisted or private playlists', async function () {
434 await createVideoPlaylist({
436 token: servers[1].accessToken,
438 displayName: 'playlist unlisted',
439 privacy: VideoPlaylistPrivacy.UNLISTED
443 await createVideoPlaylist({
445 token: servers[1].accessToken,
447 displayName: 'playlist private',
448 privacy: VideoPlaylistPrivacy.PRIVATE
452 await waitJobs(servers)
454 for (const server of servers) {
456 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
457 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
460 expect(results[0].body.total).to.equal(2)
461 expect(results[1].body.total).to.equal(3)
463 for (const res of results) {
464 const data: VideoPlaylist[] = res.body.data
465 expect(data).to.have.lengthOf(2)
466 expect(data[0].displayName).to.equal('playlist 3')
467 expect(data[1].displayName).to.equal('playlist 2')
473 describe('Update playlists', function () {
475 it('Should update a playlist', async function () {
478 await updateVideoPlaylist({
480 token: servers[1].accessToken,
482 displayName: 'playlist 3 updated',
483 description: 'description updated',
484 privacy: VideoPlaylistPrivacy.UNLISTED,
485 thumbnailfile: 'thumbnail.jpg',
486 videoChannelId: servers[1].videoChannel.id
488 playlistId: playlistServer2Id2
491 await waitJobs(servers)
493 for (const server of servers) {
494 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
495 const playlist: VideoPlaylist = res.body
497 expect(playlist.displayName).to.equal('playlist 3 updated')
498 expect(playlist.description).to.equal('description updated')
500 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
501 expect(playlist.privacy.label).to.equal('Unlisted')
503 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
504 expect(playlist.type.label).to.equal('Regular')
506 expect(playlist.videosLength).to.equal(2)
508 expect(playlist.ownerAccount.name).to.equal('root')
509 expect(playlist.ownerAccount.displayName).to.equal('root')
510 expect(playlist.videoChannel.name).to.equal('root_channel')
511 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
516 describe('Element timestamps', function () {
518 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
521 const addVideo = (elementAttrs: any) => {
522 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
525 const res = await createVideoPlaylist({
527 token: servers[0].accessToken,
529 displayName: 'playlist 4',
530 privacy: VideoPlaylistPrivacy.PUBLIC,
531 videoChannelId: servers[0].videoChannel.id
535 playlistServer1Id = res.body.videoPlaylist.id
536 playlistServer1UUID = res.body.videoPlaylist.uuid
538 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
539 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
540 await addVideo({ videoId: servers[2].videos[2].uuid })
542 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
543 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
547 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
548 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
552 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
553 playlistElementNSFW = res.body.videoPlaylistElement.id
556 await waitJobs(servers)
559 it('Should correctly list playlist videos', async function () {
562 for (const server of servers) {
563 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
565 expect(res.body.total).to.equal(6)
567 const videoElements: VideoPlaylistElement[] = res.body.data
568 expect(videoElements).to.have.lengthOf(6)
570 expect(videoElements[0].video.name).to.equal('video 0 server 1')
571 expect(videoElements[0].position).to.equal(1)
572 expect(videoElements[0].startTimestamp).to.equal(15)
573 expect(videoElements[0].stopTimestamp).to.equal(28)
575 expect(videoElements[1].video.name).to.equal('video 1 server 3')
576 expect(videoElements[1].position).to.equal(2)
577 expect(videoElements[1].startTimestamp).to.equal(35)
578 expect(videoElements[1].stopTimestamp).to.be.null
580 expect(videoElements[2].video.name).to.equal('video 2 server 3')
581 expect(videoElements[2].position).to.equal(3)
582 expect(videoElements[2].startTimestamp).to.be.null
583 expect(videoElements[2].stopTimestamp).to.be.null
585 expect(videoElements[3].video.name).to.equal('video 3 server 1')
586 expect(videoElements[3].position).to.equal(4)
587 expect(videoElements[3].startTimestamp).to.be.null
588 expect(videoElements[3].stopTimestamp).to.equal(35)
590 expect(videoElements[4].video.name).to.equal('video 4 server 1')
591 expect(videoElements[4].position).to.equal(5)
592 expect(videoElements[4].startTimestamp).to.equal(45)
593 expect(videoElements[4].stopTimestamp).to.equal(60)
595 expect(videoElements[5].video.name).to.equal('NSFW video')
596 expect(videoElements[5].position).to.equal(6)
597 expect(videoElements[5].startTimestamp).to.equal(5)
598 expect(videoElements[5].stopTimestamp).to.be.null
600 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
601 expect(res3.body.data).to.have.lengthOf(2)
606 describe('Element type', function () {
607 let groupUser1: ServerInfo[]
608 let groupWithoutToken1: ServerInfo[]
609 let group1: ServerInfo[]
610 let group2: ServerInfo[]
616 before(async function () {
619 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
620 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
621 group1 = [ servers[0] ]
622 group2 = [ servers[1], servers[2] ]
624 const res = await createVideoPlaylist({
626 token: userAccessTokenServer1,
628 displayName: 'playlist 56',
629 privacy: VideoPlaylistPrivacy.PUBLIC,
630 videoChannelId: servers[0].videoChannel.id
634 const playlistServer1Id2 = res.body.videoPlaylist.id
635 playlistServer1UUID2 = res.body.videoPlaylist.uuid
637 const addVideo = (elementAttrs: any) => {
638 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
641 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
642 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
643 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
645 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
646 await addVideo({ videoId: video2, startTimestamp: 35 })
647 await addVideo({ videoId: video3 })
649 await waitJobs(servers)
652 it('Should update the element type if the video is private', async function () {
655 const name = 'video 89'
659 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
660 await waitJobs(servers)
662 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
663 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
664 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
665 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
669 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
670 await waitJobs(servers)
672 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
673 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
674 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
675 // We deleted the video, so even if we recreated it, the old entry is still deleted
676 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
680 it('Should update the element type if the video is blacklisted', async function () {
683 const name = 'video 89'
687 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
688 await waitJobs(servers)
690 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
691 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
692 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
693 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
697 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
698 await waitJobs(servers)
700 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
701 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
702 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
703 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
704 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
708 it('Should update the element type if the account or server of the video is blocked', async function () {
711 const name = 'video 90'
715 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
716 await waitJobs(servers)
718 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
719 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
721 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
722 await waitJobs(servers)
724 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
728 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
729 await waitJobs(servers)
731 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
732 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
734 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
735 await waitJobs(servers)
737 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
741 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
742 await waitJobs(servers)
744 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
745 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
747 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
748 await waitJobs(servers)
750 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
754 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
755 await waitJobs(servers)
757 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
758 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
760 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
761 await waitJobs(servers)
763 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
767 it('Should hide the video if it is NSFW', async function () {
768 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
769 expect(res.body.total).to.equal(3)
771 const elements: VideoPlaylistElement[] = res.body.data
772 const element = elements.find(e => e.position === 3)
774 expect(element).to.exist
775 expect(element.video).to.be.null
776 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
781 describe('Managing playlist elements', function () {
783 it('Should reorder the playlist', async function () {
787 await reorderVideosPlaylist({
789 token: servers[0].accessToken,
790 playlistId: playlistServer1Id,
793 insertAfterPosition: 3
797 await waitJobs(servers)
799 for (const server of servers) {
800 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
801 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
803 expect(names).to.deep.equal([
815 await reorderVideosPlaylist({
817 token: servers[0].accessToken,
818 playlistId: playlistServer1Id,
822 insertAfterPosition: 4
826 await waitJobs(servers)
828 for (const server of servers) {
829 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
830 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
832 expect(names).to.deep.equal([
844 await reorderVideosPlaylist({
846 token: servers[0].accessToken,
847 playlistId: playlistServer1Id,
850 insertAfterPosition: 3
854 await waitJobs(servers)
856 for (const server of servers) {
857 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
858 const elements: VideoPlaylistElement[] = res.body.data
859 const names = elements.map(v => v.video.name)
861 expect(names).to.deep.equal([
870 for (let i = 1; i <= elements.length; i++) {
871 expect(elements[i - 1].position).to.equal(i)
877 it('Should update startTimestamp/endTimestamp of some elements', async function () {
880 await updateVideoPlaylistElement({
882 token: servers[0].accessToken,
883 playlistId: playlistServer1Id,
884 playlistElementId: playlistElementServer1Video4,
890 await updateVideoPlaylistElement({
892 token: servers[0].accessToken,
893 playlistId: playlistServer1Id,
894 playlistElementId: playlistElementServer1Video5,
900 await waitJobs(servers)
902 for (const server of servers) {
903 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
904 const elements: VideoPlaylistElement[] = res.body.data
906 expect(elements[0].video.name).to.equal('video 3 server 1')
907 expect(elements[0].position).to.equal(1)
908 expect(elements[0].startTimestamp).to.equal(1)
909 expect(elements[0].stopTimestamp).to.equal(35)
911 expect(elements[5].video.name).to.equal('video 4 server 1')
912 expect(elements[5].position).to.equal(6)
913 expect(elements[5].startTimestamp).to.equal(45)
914 expect(elements[5].stopTimestamp).to.be.null
918 it('Should check videos existence in my playlist', async function () {
920 servers[0].videos[0].id,
922 servers[0].videos[3].id,
924 servers[0].videos[4].id
926 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
927 const obj = res.body as VideoExistInPlaylist
930 const elem = obj[servers[0].videos[0].id]
931 expect(elem).to.have.lengthOf(1)
932 expect(elem[0].playlistElementId).to.exist
933 expect(elem[0].playlistId).to.equal(playlistServer1Id)
934 expect(elem[0].startTimestamp).to.equal(15)
935 expect(elem[0].stopTimestamp).to.equal(28)
939 const elem = obj[servers[0].videos[3].id]
940 expect(elem).to.have.lengthOf(1)
941 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
942 expect(elem[0].playlistId).to.equal(playlistServer1Id)
943 expect(elem[0].startTimestamp).to.equal(1)
944 expect(elem[0].stopTimestamp).to.equal(35)
948 const elem = obj[servers[0].videos[4].id]
949 expect(elem).to.have.lengthOf(1)
950 expect(elem[0].playlistId).to.equal(playlistServer1Id)
951 expect(elem[0].startTimestamp).to.equal(45)
952 expect(elem[0].stopTimestamp).to.equal(null)
955 expect(obj[42000]).to.have.lengthOf(0)
956 expect(obj[43000]).to.have.lengthOf(0)
959 it('Should automatically update updatedAt field of playlists', async function () {
960 const server = servers[1]
961 const videoId = servers[1].videos[5].id
963 async function getPlaylistNames () {
964 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
966 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
969 const elementAttrs = { videoId }
970 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
971 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
973 const element1 = res1.body.videoPlaylistElement.id
974 const element2 = res2.body.videoPlaylistElement.id
976 const names1 = await getPlaylistNames()
977 expect(names1[0]).to.equal('playlist 3 updated')
978 expect(names1[1]).to.equal('playlist 2')
980 await removeVideoFromPlaylist({
982 token: server.accessToken,
983 playlistId: playlistServer2Id1,
984 playlistElementId: element1
987 const names2 = await getPlaylistNames()
988 expect(names2[0]).to.equal('playlist 2')
989 expect(names2[1]).to.equal('playlist 3 updated')
991 await removeVideoFromPlaylist({
993 token: server.accessToken,
994 playlistId: playlistServer2Id2,
995 playlistElementId: element2
998 const names3 = await getPlaylistNames()
999 expect(names3[0]).to.equal('playlist 3 updated')
1000 expect(names3[1]).to.equal('playlist 2')
1003 it('Should delete some elements', async function () {
1006 await removeVideoFromPlaylist({
1007 url: servers[0].url,
1008 token: servers[0].accessToken,
1009 playlistId: playlistServer1Id,
1010 playlistElementId: playlistElementServer1Video4
1013 await removeVideoFromPlaylist({
1014 url: servers[0].url,
1015 token: servers[0].accessToken,
1016 playlistId: playlistServer1Id,
1017 playlistElementId: playlistElementNSFW
1020 await waitJobs(servers)
1022 for (const server of servers) {
1023 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1025 expect(res.body.total).to.equal(4)
1027 const elements: VideoPlaylistElement[] = res.body.data
1028 expect(elements).to.have.lengthOf(4)
1030 expect(elements[0].video.name).to.equal('video 0 server 1')
1031 expect(elements[0].position).to.equal(1)
1033 expect(elements[1].video.name).to.equal('video 2 server 3')
1034 expect(elements[1].position).to.equal(2)
1036 expect(elements[2].video.name).to.equal('video 1 server 3')
1037 expect(elements[2].position).to.equal(3)
1039 expect(elements[3].video.name).to.equal('video 4 server 1')
1040 expect(elements[3].position).to.equal(4)
1044 it('Should be able to create a public playlist, and set it to private', async function () {
1047 const res = await createVideoPlaylist({
1048 url: servers[0].url,
1049 token: servers[0].accessToken,
1051 displayName: 'my super public playlist',
1052 privacy: VideoPlaylistPrivacy.PUBLIC,
1053 videoChannelId: servers[0].videoChannel.id
1056 const videoPlaylistIds = res.body.videoPlaylist
1058 await waitJobs(servers)
1060 for (const server of servers) {
1061 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 200)
1064 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1065 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1067 await waitJobs(servers)
1069 for (const server of [ servers[1], servers[2] ]) {
1070 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 404)
1072 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, 401)
1074 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, 200)
1078 describe('Playlist deletion', function () {
1080 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1083 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1085 await waitJobs(servers)
1087 for (const server of servers) {
1088 await getVideoPlaylist(server.url, playlistServer1UUID, 404)
1092 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1095 for (const server of servers) {
1096 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1100 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1103 const finder = data => data.find(p => p.displayName === 'my super playlist')
1106 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1107 expect(res.body.total).to.equal(3)
1108 expect(finder(res.body.data)).to.not.be.undefined
1111 await unfollow(servers[2].url, servers[2].accessToken, servers[0])
1114 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1115 expect(res.body.total).to.equal(1)
1117 expect(finder(res.body.data)).to.be.undefined
1121 it('Should delete a channel and put the associated playlist in private mode', async function () {
1124 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1125 const videoChannelId = res.body.videoChannel.id
1127 const res2 = await createVideoPlaylist({
1128 url: servers[0].url,
1129 token: servers[0].accessToken,
1131 displayName: 'channel playlist',
1132 privacy: VideoPlaylistPrivacy.PUBLIC,
1136 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1138 await waitJobs(servers)
1140 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1142 await waitJobs(servers)
1144 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1145 expect(res3.body.displayName).to.equal('channel playlist')
1146 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1148 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, 404)
1151 it('Should delete an account and delete its playlists', async function () {
1154 const user = { username: 'user_1', password: 'password' }
1155 const res = await createUser({
1156 url: servers[0].url,
1157 accessToken: servers[0].accessToken,
1158 username: user.username,
1159 password: user.password
1162 const userId = res.body.user.id
1163 const userAccessToken = await userLogin(servers[0], user)
1165 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1166 const userChannel = (resChannel.body as User).videoChannels[0]
1168 await createVideoPlaylist({
1169 url: servers[0].url,
1170 token: userAccessToken,
1172 displayName: 'playlist to be deleted',
1173 privacy: VideoPlaylistPrivacy.PUBLIC,
1174 videoChannelId: userChannel.id
1178 await waitJobs(servers)
1180 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1183 for (const server of [ servers[0], servers[1] ]) {
1184 const res = await getVideoPlaylistsList(server.url, 0, 15)
1185 expect(finder(res.body.data)).to.not.be.undefined
1189 await removeUser(servers[0].url, userId, servers[0].accessToken)
1190 await waitJobs(servers)
1193 for (const server of [ servers[0], servers[1] ]) {
1194 const res = await getVideoPlaylistsList(server.url, 0, 15)
1195 expect(finder(res.body.data)).to.be.undefined
1201 after(async function () {
1202 await cleanupTests(servers)