1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import * as chai from 'chai'
5 import { HttpStatusCode } from '@shared/core-utils'
7 addAccountToAccountBlocklist,
8 addAccountToServerBlocklist,
9 addServerToAccountBlocklist,
10 addServerToServerBlocklist,
14 checkPlaylistFilesWereRemoved,
21 doVideosExistInMyPlaylist,
22 flushAndRunMultipleServers,
23 generateUserAccessToken,
25 getAccountPlaylistsList,
26 getAccountPlaylistsListWithToken,
29 getVideoChannelPlaylistsList,
31 getVideoPlaylistPrivacies,
32 getVideoPlaylistsList,
33 getVideoPlaylistWithToken,
34 removeAccountFromAccountBlocklist,
35 removeAccountFromServerBlocklist,
36 removeServerFromAccountBlocklist,
37 removeServerFromServerBlocklist,
39 removeVideoFromBlacklist,
40 removeVideoFromPlaylist,
41 reorderVideosPlaylist,
43 setAccessTokensToServers,
44 setDefaultVideoChannel,
48 updateVideoPlaylistElement,
54 } from '@shared/extra-utils'
59 VideoPlaylistCreateResult,
61 VideoPlaylistElementType,
65 } from '@shared/models'
67 const expect = chai.expect
69 async function checkPlaylistElementType (
70 servers: ServerInfo[],
72 type: VideoPlaylistElementType,
77 for (const server of servers) {
78 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10)
79 expect(res.body.total).to.equal(total)
81 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position)
82 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
84 if (type === VideoPlaylistElementType.REGULAR) {
85 expect(videoElement.video).to.not.be.null
86 expect(videoElement.video.name).to.equal(name)
88 expect(videoElement.video).to.be.null
93 describe('Test video playlists', function () {
94 let servers: ServerInfo[] = []
96 let playlistServer2Id1: number
97 let playlistServer2Id2: number
98 let playlistServer2UUID2: number
100 let playlistServer1Id: number
101 let playlistServer1UUID: string
102 let playlistServer1UUID2: string
104 let playlistElementServer1Video4: number
105 let playlistElementServer1Video5: number
106 let playlistElementNSFW: number
108 let nsfwVideoServer1: number
110 let userAccessTokenServer1: string
112 before(async function () {
115 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
117 // Get the access tokens
118 await setAccessTokensToServers(servers)
119 await setDefaultVideoChannel(servers)
121 // Server 1 and server 2 follow each other
122 await doubleFollow(servers[0], servers[1])
123 // Server 1 and server 3 follow each other
124 await doubleFollow(servers[0], servers[2])
127 servers[0].videos = []
128 servers[1].videos = []
129 servers[2].videos = []
131 for (const server of servers) {
132 for (let i = 0; i < 7; i++) {
133 const name = `video ${i} server ${server.serverNumber}`
134 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
136 server.videos.push(resVideo.body.video)
141 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
146 accessToken: servers[0].accessToken,
150 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
153 await waitJobs(servers)
156 describe('Get default playlists', function () {
157 it('Should list video playlist privacies', async function () {
158 const res = await getVideoPlaylistPrivacies(servers[0].url)
160 const privacies = res.body
161 expect(Object.keys(privacies)).to.have.length.at.least(3)
163 expect(privacies[3]).to.equal('Private')
166 it('Should list watch later playlist', async function () {
167 const url = servers[0].url
168 const accessToken = servers[0].accessToken
171 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
173 expect(res.body.total).to.equal(1)
174 expect(res.body.data).to.have.lengthOf(1)
176 const playlist: VideoPlaylist = res.body.data[0]
177 expect(playlist.displayName).to.equal('Watch later')
178 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
179 expect(playlist.type.label).to.equal('Watch later')
183 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
185 expect(res.body.total).to.equal(0)
186 expect(res.body.data).to.have.lengthOf(0)
190 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
191 expect(res.body.total).to.equal(0)
192 expect(res.body.data).to.have.lengthOf(0)
196 it('Should get private playlist for a classic user', async function () {
197 const token = await generateUserAccessToken(servers[0], 'toto')
199 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
201 expect(res.body.total).to.equal(1)
202 expect(res.body.data).to.have.lengthOf(1)
204 const playlistId = res.body.data[0].id
205 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
209 describe('Create and federate playlists', function () {
211 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
214 await createVideoPlaylist({
216 token: servers[0].accessToken,
218 displayName: 'my super playlist',
219 privacy: VideoPlaylistPrivacy.PUBLIC,
220 description: 'my super description',
221 thumbnailfile: 'thumbnail.jpg',
222 videoChannelId: servers[0].videoChannel.id
226 await waitJobs(servers)
227 // Processing a playlist by the receiver could be long
230 for (const server of servers) {
231 const res = await getVideoPlaylistsList(server.url, 0, 5)
232 expect(res.body.total).to.equal(1)
233 expect(res.body.data).to.have.lengthOf(1)
235 const playlistFromList = res.body.data[0] as VideoPlaylist
237 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
238 const playlistFromGet = res2.body as VideoPlaylist
240 for (const playlist of [ playlistFromGet, playlistFromList ]) {
241 expect(playlist.id).to.be.a('number')
242 expect(playlist.uuid).to.be.a('string')
244 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
246 expect(playlist.displayName).to.equal('my super playlist')
247 expect(playlist.description).to.equal('my super description')
248 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
249 expect(playlist.privacy.label).to.equal('Public')
250 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
251 expect(playlist.type.label).to.equal('Regular')
252 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
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)
314 for (const server of [ servers[0], servers[1] ]) {
315 const res = await getVideoPlaylistsList(server.url, 0, 5)
317 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
318 expect(playlist2).to.not.be.undefined
319 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
321 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
322 expect(playlist3).to.not.be.undefined
323 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
326 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
327 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
328 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
331 it('Should have the playlist on server 3 after a new follow', async function () {
334 // Server 2 and server 3 follow each other
335 await doubleFollow(servers[1], servers[2])
337 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
339 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
340 expect(playlist2).to.not.be.undefined
341 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
343 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
347 describe('List playlists', function () {
349 it('Should correctly list the playlists', async function () {
353 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt')
355 expect(res.body.total).to.equal(3)
357 const data: VideoPlaylist[] = res.body.data
358 expect(data).to.have.lengthOf(2)
359 expect(data[0].displayName).to.equal('playlist 2')
360 expect(data[1].displayName).to.equal('playlist 3')
364 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt')
366 expect(res.body.total).to.equal(3)
368 const data: VideoPlaylist[] = res.body.data
369 expect(data).to.have.lengthOf(2)
370 expect(data[0].displayName).to.equal('playlist 2')
371 expect(data[1].displayName).to.equal('my super playlist')
375 it('Should list video channel playlists', async function () {
379 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt')
381 expect(res.body.total).to.equal(1)
383 const data: VideoPlaylist[] = res.body.data
384 expect(data).to.have.lengthOf(1)
385 expect(data[0].displayName).to.equal('my super playlist')
389 it('Should list account playlists', async function () {
393 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt')
395 expect(res.body.total).to.equal(2)
397 const data: VideoPlaylist[] = res.body.data
398 expect(data).to.have.lengthOf(1)
399 expect(data[0].displayName).to.equal('playlist 2')
403 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt')
405 expect(res.body.total).to.equal(2)
407 const data: VideoPlaylist[] = res.body.data
408 expect(data).to.have.lengthOf(1)
409 expect(data[0].displayName).to.equal('playlist 3')
413 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3')
415 expect(res.body.total).to.equal(1)
417 const data: VideoPlaylist[] = res.body.data
418 expect(data).to.have.lengthOf(1)
419 expect(data[0].displayName).to.equal('playlist 3')
423 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4')
425 expect(res.body.total).to.equal(0)
427 const data: VideoPlaylist[] = res.body.data
428 expect(data).to.have.lengthOf(0)
433 describe('Playlist rights', function () {
434 let unlistedPlaylist: VideoPlaylistCreateResult
435 let privatePlaylist: VideoPlaylistCreateResult
437 before(async function () {
441 const res = await createVideoPlaylist({
443 token: servers[1].accessToken,
445 displayName: 'playlist unlisted',
446 privacy: VideoPlaylistPrivacy.UNLISTED,
447 videoChannelId: servers[1].videoChannel.id
450 unlistedPlaylist = res.body.videoPlaylist
454 const res = await createVideoPlaylist({
456 token: servers[1].accessToken,
458 displayName: 'playlist private',
459 privacy: VideoPlaylistPrivacy.PRIVATE
462 privatePlaylist = res.body.videoPlaylist
465 await waitJobs(servers)
469 it('Should not list unlisted or private playlists', async function () {
470 for (const server of servers) {
472 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
473 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
476 expect(results[0].body.total).to.equal(2)
477 expect(results[1].body.total).to.equal(3)
479 for (const res of results) {
480 const data: VideoPlaylist[] = res.body.data
481 expect(data).to.have.lengthOf(2)
482 expect(data[0].displayName).to.equal('playlist 3')
483 expect(data[1].displayName).to.equal('playlist 2')
488 it('Should not get unlisted playlist using only the id', async function () {
489 await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404)
492 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
493 await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid)
494 await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID)
497 it('Should not get private playlist without token', async function () {
498 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
499 await getVideoPlaylist(servers[1].url, id, 401)
503 it('Should get private playlist with a token', async function () {
504 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
505 await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id)
510 describe('Update playlists', function () {
512 it('Should update a playlist', async function () {
515 await updateVideoPlaylist({
517 token: servers[1].accessToken,
519 displayName: 'playlist 3 updated',
520 description: 'description updated',
521 privacy: VideoPlaylistPrivacy.UNLISTED,
522 thumbnailfile: 'thumbnail.jpg',
523 videoChannelId: servers[1].videoChannel.id
525 playlistId: playlistServer2Id2
528 await waitJobs(servers)
530 for (const server of servers) {
531 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
532 const playlist: VideoPlaylist = res.body
534 expect(playlist.displayName).to.equal('playlist 3 updated')
535 expect(playlist.description).to.equal('description updated')
537 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
538 expect(playlist.privacy.label).to.equal('Unlisted')
540 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
541 expect(playlist.type.label).to.equal('Regular')
543 expect(playlist.videosLength).to.equal(2)
545 expect(playlist.ownerAccount.name).to.equal('root')
546 expect(playlist.ownerAccount.displayName).to.equal('root')
547 expect(playlist.videoChannel.name).to.equal('root_channel')
548 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
553 describe('Element timestamps', function () {
555 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
558 const addVideo = (elementAttrs: any) => {
559 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
562 const res = await createVideoPlaylist({
564 token: servers[0].accessToken,
566 displayName: 'playlist 4',
567 privacy: VideoPlaylistPrivacy.PUBLIC,
568 videoChannelId: servers[0].videoChannel.id
572 playlistServer1Id = res.body.videoPlaylist.id
573 playlistServer1UUID = res.body.videoPlaylist.uuid
575 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
576 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
577 await addVideo({ videoId: servers[2].videos[2].uuid })
579 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
580 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
584 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
585 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
589 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
590 playlistElementNSFW = res.body.videoPlaylistElement.id
592 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
593 await addVideo({ videoId: nsfwVideoServer1 })
596 await waitJobs(servers)
599 it('Should correctly list playlist videos', async function () {
602 for (const server of servers) {
603 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
605 expect(res.body.total).to.equal(8)
607 const videoElements: VideoPlaylistElement[] = res.body.data
608 expect(videoElements).to.have.lengthOf(8)
610 expect(videoElements[0].video.name).to.equal('video 0 server 1')
611 expect(videoElements[0].position).to.equal(1)
612 expect(videoElements[0].startTimestamp).to.equal(15)
613 expect(videoElements[0].stopTimestamp).to.equal(28)
615 expect(videoElements[1].video.name).to.equal('video 1 server 3')
616 expect(videoElements[1].position).to.equal(2)
617 expect(videoElements[1].startTimestamp).to.equal(35)
618 expect(videoElements[1].stopTimestamp).to.be.null
620 expect(videoElements[2].video.name).to.equal('video 2 server 3')
621 expect(videoElements[2].position).to.equal(3)
622 expect(videoElements[2].startTimestamp).to.be.null
623 expect(videoElements[2].stopTimestamp).to.be.null
625 expect(videoElements[3].video.name).to.equal('video 3 server 1')
626 expect(videoElements[3].position).to.equal(4)
627 expect(videoElements[3].startTimestamp).to.be.null
628 expect(videoElements[3].stopTimestamp).to.equal(35)
630 expect(videoElements[4].video.name).to.equal('video 4 server 1')
631 expect(videoElements[4].position).to.equal(5)
632 expect(videoElements[4].startTimestamp).to.equal(45)
633 expect(videoElements[4].stopTimestamp).to.equal(60)
635 expect(videoElements[5].video.name).to.equal('NSFW video')
636 expect(videoElements[5].position).to.equal(6)
637 expect(videoElements[5].startTimestamp).to.equal(5)
638 expect(videoElements[5].stopTimestamp).to.be.null
640 expect(videoElements[6].video.name).to.equal('NSFW video')
641 expect(videoElements[6].position).to.equal(7)
642 expect(videoElements[6].startTimestamp).to.equal(4)
643 expect(videoElements[6].stopTimestamp).to.be.null
645 expect(videoElements[7].video.name).to.equal('NSFW video')
646 expect(videoElements[7].position).to.equal(8)
647 expect(videoElements[7].startTimestamp).to.be.null
648 expect(videoElements[7].stopTimestamp).to.be.null
650 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
651 expect(res3.body.data).to.have.lengthOf(2)
656 describe('Element type', function () {
657 let groupUser1: ServerInfo[]
658 let groupWithoutToken1: ServerInfo[]
659 let group1: ServerInfo[]
660 let group2: ServerInfo[]
666 before(async function () {
669 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
670 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
671 group1 = [ servers[0] ]
672 group2 = [ servers[1], servers[2] ]
674 const res = await createVideoPlaylist({
676 token: userAccessTokenServer1,
678 displayName: 'playlist 56',
679 privacy: VideoPlaylistPrivacy.PUBLIC,
680 videoChannelId: servers[0].videoChannel.id
684 const playlistServer1Id2 = res.body.videoPlaylist.id
685 playlistServer1UUID2 = res.body.videoPlaylist.uuid
687 const addVideo = (elementAttrs: any) => {
688 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
691 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
692 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
693 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
695 await waitJobs(servers)
697 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
698 await addVideo({ videoId: video2, startTimestamp: 35 })
699 await addVideo({ videoId: video3 })
701 await waitJobs(servers)
704 it('Should update the element type if the video is private', async function () {
707 const name = 'video 89'
711 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
712 await waitJobs(servers)
714 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
715 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
716 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
717 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
721 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
722 await waitJobs(servers)
724 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
725 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
726 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
727 // We deleted the video, so even if we recreated it, the old entry is still deleted
728 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
732 it('Should update the element type if the video is blacklisted', async function () {
735 const name = 'video 89'
739 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
740 await waitJobs(servers)
742 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
743 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
744 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
745 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
749 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
750 await waitJobs(servers)
752 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
753 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
754 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
755 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
756 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
760 it('Should update the element type if the account or server of the video is blocked', async function () {
763 const name = 'video 90'
767 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
768 await waitJobs(servers)
770 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
771 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
773 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
774 await waitJobs(servers)
776 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
780 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
781 await waitJobs(servers)
783 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
784 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
786 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
787 await waitJobs(servers)
789 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
793 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
794 await waitJobs(servers)
796 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
797 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
799 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
800 await waitJobs(servers)
802 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
806 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
807 await waitJobs(servers)
809 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
810 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
812 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
813 await waitJobs(servers)
815 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
819 it('Should hide the video if it is NSFW', async function () {
820 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
821 expect(res.body.total).to.equal(3)
823 const elements: VideoPlaylistElement[] = res.body.data
824 const element = elements.find(e => e.position === 3)
826 expect(element).to.exist
827 expect(element.video).to.be.null
828 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
833 describe('Managing playlist elements', function () {
835 it('Should reorder the playlist', async function () {
839 await reorderVideosPlaylist({
841 token: servers[0].accessToken,
842 playlistId: playlistServer1Id,
845 insertAfterPosition: 3
849 await waitJobs(servers)
851 for (const server of servers) {
852 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
853 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
855 expect(names).to.deep.equal([
869 await reorderVideosPlaylist({
871 token: servers[0].accessToken,
872 playlistId: playlistServer1Id,
876 insertAfterPosition: 4
880 await waitJobs(servers)
882 for (const server of servers) {
883 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
884 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
886 expect(names).to.deep.equal([
900 await reorderVideosPlaylist({
902 token: servers[0].accessToken,
903 playlistId: playlistServer1Id,
906 insertAfterPosition: 3
910 await waitJobs(servers)
912 for (const server of servers) {
913 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
914 const elements: VideoPlaylistElement[] = res.body.data
915 const names = elements.map(v => v.video.name)
917 expect(names).to.deep.equal([
928 for (let i = 1; i <= elements.length; i++) {
929 expect(elements[i - 1].position).to.equal(i)
935 it('Should update startTimestamp/endTimestamp of some elements', async function () {
938 await updateVideoPlaylistElement({
940 token: servers[0].accessToken,
941 playlistId: playlistServer1Id,
942 playlistElementId: playlistElementServer1Video4,
948 await updateVideoPlaylistElement({
950 token: servers[0].accessToken,
951 playlistId: playlistServer1Id,
952 playlistElementId: playlistElementServer1Video5,
958 await waitJobs(servers)
960 for (const server of servers) {
961 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
962 const elements: VideoPlaylistElement[] = res.body.data
964 expect(elements[0].video.name).to.equal('video 3 server 1')
965 expect(elements[0].position).to.equal(1)
966 expect(elements[0].startTimestamp).to.equal(1)
967 expect(elements[0].stopTimestamp).to.equal(35)
969 expect(elements[5].video.name).to.equal('video 4 server 1')
970 expect(elements[5].position).to.equal(6)
971 expect(elements[5].startTimestamp).to.equal(45)
972 expect(elements[5].stopTimestamp).to.be.null
976 it('Should check videos existence in my playlist', async function () {
978 servers[0].videos[0].id,
980 servers[0].videos[3].id,
982 servers[0].videos[4].id
984 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
985 const obj = res.body as VideoExistInPlaylist
988 const elem = obj[servers[0].videos[0].id]
989 expect(elem).to.have.lengthOf(1)
990 expect(elem[0].playlistElementId).to.exist
991 expect(elem[0].playlistId).to.equal(playlistServer1Id)
992 expect(elem[0].startTimestamp).to.equal(15)
993 expect(elem[0].stopTimestamp).to.equal(28)
997 const elem = obj[servers[0].videos[3].id]
998 expect(elem).to.have.lengthOf(1)
999 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
1000 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1001 expect(elem[0].startTimestamp).to.equal(1)
1002 expect(elem[0].stopTimestamp).to.equal(35)
1006 const elem = obj[servers[0].videos[4].id]
1007 expect(elem).to.have.lengthOf(1)
1008 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1009 expect(elem[0].startTimestamp).to.equal(45)
1010 expect(elem[0].stopTimestamp).to.equal(null)
1013 expect(obj[42000]).to.have.lengthOf(0)
1014 expect(obj[43000]).to.have.lengthOf(0)
1017 it('Should automatically update updatedAt field of playlists', async function () {
1018 const server = servers[1]
1019 const videoId = servers[1].videos[5].id
1021 async function getPlaylistNames () {
1022 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
1024 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
1027 const elementAttrs = { videoId }
1028 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
1029 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
1031 const element1 = res1.body.videoPlaylistElement.id
1032 const element2 = res2.body.videoPlaylistElement.id
1034 const names1 = await getPlaylistNames()
1035 expect(names1[0]).to.equal('playlist 3 updated')
1036 expect(names1[1]).to.equal('playlist 2')
1038 await removeVideoFromPlaylist({
1040 token: server.accessToken,
1041 playlistId: playlistServer2Id1,
1042 playlistElementId: element1
1045 const names2 = await getPlaylistNames()
1046 expect(names2[0]).to.equal('playlist 2')
1047 expect(names2[1]).to.equal('playlist 3 updated')
1049 await removeVideoFromPlaylist({
1051 token: server.accessToken,
1052 playlistId: playlistServer2Id2,
1053 playlistElementId: element2
1056 const names3 = await getPlaylistNames()
1057 expect(names3[0]).to.equal('playlist 3 updated')
1058 expect(names3[1]).to.equal('playlist 2')
1061 it('Should delete some elements', async function () {
1064 await removeVideoFromPlaylist({
1065 url: servers[0].url,
1066 token: servers[0].accessToken,
1067 playlistId: playlistServer1Id,
1068 playlistElementId: playlistElementServer1Video4
1071 await removeVideoFromPlaylist({
1072 url: servers[0].url,
1073 token: servers[0].accessToken,
1074 playlistId: playlistServer1Id,
1075 playlistElementId: playlistElementNSFW
1078 await waitJobs(servers)
1080 for (const server of servers) {
1081 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1083 expect(res.body.total).to.equal(6)
1085 const elements: VideoPlaylistElement[] = res.body.data
1086 expect(elements).to.have.lengthOf(6)
1088 expect(elements[0].video.name).to.equal('video 0 server 1')
1089 expect(elements[0].position).to.equal(1)
1091 expect(elements[1].video.name).to.equal('video 2 server 3')
1092 expect(elements[1].position).to.equal(2)
1094 expect(elements[2].video.name).to.equal('video 1 server 3')
1095 expect(elements[2].position).to.equal(3)
1097 expect(elements[3].video.name).to.equal('video 4 server 1')
1098 expect(elements[3].position).to.equal(4)
1100 expect(elements[4].video.name).to.equal('NSFW video')
1101 expect(elements[4].position).to.equal(5)
1103 expect(elements[5].video.name).to.equal('NSFW video')
1104 expect(elements[5].position).to.equal(6)
1108 it('Should be able to create a public playlist, and set it to private', async function () {
1111 const res = await createVideoPlaylist({
1112 url: servers[0].url,
1113 token: servers[0].accessToken,
1115 displayName: 'my super public playlist',
1116 privacy: VideoPlaylistPrivacy.PUBLIC,
1117 videoChannelId: servers[0].videoChannel.id
1120 const videoPlaylistIds = res.body.videoPlaylist
1122 await waitJobs(servers)
1124 for (const server of servers) {
1125 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1128 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1129 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1131 await waitJobs(servers)
1133 for (const server of [ servers[1], servers[2] ]) {
1134 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404)
1136 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1138 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1142 describe('Playlist deletion', function () {
1144 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1147 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1149 await waitJobs(servers)
1151 for (const server of servers) {
1152 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404)
1156 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1159 for (const server of servers) {
1160 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1164 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1167 const finder = data => data.find(p => p.displayName === 'my super playlist')
1170 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1171 expect(res.body.total).to.equal(3)
1172 expect(finder(res.body.data)).to.not.be.undefined
1175 await servers[2].followsCommand.unfollow({ target: servers[0] })
1178 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1179 expect(res.body.total).to.equal(1)
1181 expect(finder(res.body.data)).to.be.undefined
1185 it('Should delete a channel and put the associated playlist in private mode', async function () {
1188 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1189 const videoChannelId = res.body.videoChannel.id
1191 const res2 = await createVideoPlaylist({
1192 url: servers[0].url,
1193 token: servers[0].accessToken,
1195 displayName: 'channel playlist',
1196 privacy: VideoPlaylistPrivacy.PUBLIC,
1200 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1202 await waitJobs(servers)
1204 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1206 await waitJobs(servers)
1208 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1209 expect(res3.body.displayName).to.equal('channel playlist')
1210 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1212 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404)
1215 it('Should delete an account and delete its playlists', async function () {
1218 const user = { username: 'user_1', password: 'password' }
1219 const res = await createUser({
1220 url: servers[0].url,
1221 accessToken: servers[0].accessToken,
1222 username: user.username,
1223 password: user.password
1226 const userId = res.body.user.id
1227 const userAccessToken = await userLogin(servers[0], user)
1229 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1230 const userChannel = (resChannel.body as User).videoChannels[0]
1232 await createVideoPlaylist({
1233 url: servers[0].url,
1234 token: userAccessToken,
1236 displayName: 'playlist to be deleted',
1237 privacy: VideoPlaylistPrivacy.PUBLIC,
1238 videoChannelId: userChannel.id
1242 await waitJobs(servers)
1244 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1247 for (const server of [ servers[0], servers[1] ]) {
1248 const res = await getVideoPlaylistsList(server.url, 0, 15)
1249 expect(finder(res.body.data)).to.not.be.undefined
1253 await removeUser(servers[0].url, userId, servers[0].accessToken)
1254 await waitJobs(servers)
1257 for (const server of [ servers[0], servers[1] ]) {
1258 const res = await getVideoPlaylistsList(server.url, 0, 15)
1259 expect(finder(res.body.data)).to.be.undefined
1265 after(async function () {
1266 await cleanupTests(servers)