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,
46 } from '../../../../shared/extra-utils'
47 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
48 import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
49 import { VideoPrivacy } from '../../../../shared/models/videos'
50 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
51 import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
52 import { User } from '../../../../shared/models/users'
53 import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
55 addAccountToAccountBlocklist,
56 addAccountToServerBlocklist,
57 addServerToAccountBlocklist,
58 addServerToServerBlocklist,
59 removeAccountFromAccountBlocklist,
60 removeAccountFromServerBlocklist,
61 removeServerFromAccountBlocklist,
62 removeServerFromServerBlocklist
63 } from '../../../../shared/extra-utils/users/blocklist'
65 const expect = chai.expect
67 async function checkPlaylistElementType (
68 servers: ServerInfo[],
70 type: VideoPlaylistElementType,
75 for (const server of servers) {
76 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10)
77 expect(res.body.total).to.equal(total)
79 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position)
80 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
82 if (type === VideoPlaylistElementType.REGULAR) {
83 expect(videoElement.video).to.not.be.null
84 expect(videoElement.video.name).to.equal(name)
86 expect(videoElement.video).to.be.null
91 describe('Test video playlists', function () {
92 let servers: ServerInfo[] = []
94 let playlistServer2Id1: number
95 let playlistServer2Id2: number
96 let playlistServer2UUID2: number
98 let playlistServer1Id: number
99 let playlistServer1UUID: string
100 let playlistServer1UUID2: string
102 let playlistElementServer1Video4: number
103 let playlistElementServer1Video5: number
104 let playlistElementNSFW: number
106 let nsfwVideoServer1: number
108 let userAccessTokenServer1: string
110 before(async function () {
113 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
115 // Get the access tokens
116 await setAccessTokensToServers(servers)
117 await setDefaultVideoChannel(servers)
119 // Server 1 and server 2 follow each other
120 await doubleFollow(servers[0], servers[1])
121 // Server 1 and server 3 follow each other
122 await doubleFollow(servers[0], servers[2])
125 const serverPromises: Promise<any>[][] = []
127 for (const server of servers) {
128 const videoPromises: Promise<any>[] = []
130 for (let i = 0; i < 7; i++) {
132 uploadVideo(server.url, server.accessToken, { name: `video ${i} server ${server.serverNumber}`, nsfw: false })
133 .then(res => res.body.video)
137 serverPromises.push(videoPromises)
140 servers[0].videos = await Promise.all(serverPromises[0])
141 servers[1].videos = await Promise.all(serverPromises[1])
142 servers[2].videos = await Promise.all(serverPromises[2])
145 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
150 accessToken: servers[0].accessToken,
154 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
157 await waitJobs(servers)
160 describe('Get default playlists', function () {
161 it('Should list video playlist privacies', async function () {
162 const res = await getVideoPlaylistPrivacies(servers[0].url)
164 const privacies = res.body
165 expect(Object.keys(privacies)).to.have.length.at.least(3)
167 expect(privacies[3]).to.equal('Private')
170 it('Should list watch later playlist', async function () {
171 const url = servers[0].url
172 const accessToken = servers[0].accessToken
175 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
177 expect(res.body.total).to.equal(1)
178 expect(res.body.data).to.have.lengthOf(1)
180 const playlist: VideoPlaylist = res.body.data[0]
181 expect(playlist.displayName).to.equal('Watch later')
182 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
183 expect(playlist.type.label).to.equal('Watch later')
187 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
189 expect(res.body.total).to.equal(0)
190 expect(res.body.data).to.have.lengthOf(0)
194 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
195 expect(res.body.total).to.equal(0)
196 expect(res.body.data).to.have.lengthOf(0)
200 it('Should get private playlist for a classic user', async function () {
201 const token = await generateUserAccessToken(servers[0], 'toto')
203 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
205 expect(res.body.total).to.equal(1)
206 expect(res.body.data).to.have.lengthOf(1)
208 const playlistId = res.body.data[0].id
209 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
213 describe('Create and federate playlists', function () {
215 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
218 await createVideoPlaylist({
220 token: servers[0].accessToken,
222 displayName: 'my super playlist',
223 privacy: VideoPlaylistPrivacy.PUBLIC,
224 description: 'my super description',
225 thumbnailfile: 'thumbnail.jpg',
226 videoChannelId: servers[0].videoChannel.id
230 await waitJobs(servers)
231 // Processing a playlist by the receiver could be long
234 for (const server of servers) {
235 const res = await getVideoPlaylistsList(server.url, 0, 5)
236 expect(res.body.total).to.equal(1)
237 expect(res.body.data).to.have.lengthOf(1)
239 const playlistFromList = res.body.data[0] as VideoPlaylist
241 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
242 const playlistFromGet = res2.body as VideoPlaylist
244 for (const playlist of [ playlistFromGet, playlistFromList ]) {
245 expect(playlist.id).to.be.a('number')
246 expect(playlist.uuid).to.be.a('string')
248 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
250 expect(playlist.displayName).to.equal('my super playlist')
251 expect(playlist.description).to.equal('my super description')
252 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
253 expect(playlist.privacy.label).to.equal('Public')
254 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
255 expect(playlist.type.label).to.equal('Regular')
256 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
258 expect(playlist.videosLength).to.equal(0)
260 expect(playlist.ownerAccount.name).to.equal('root')
261 expect(playlist.ownerAccount.displayName).to.equal('root')
262 expect(playlist.videoChannel.name).to.equal('root_channel')
263 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
268 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
272 const res = await createVideoPlaylist({
274 token: servers[1].accessToken,
276 displayName: 'playlist 2',
277 privacy: VideoPlaylistPrivacy.PUBLIC,
278 videoChannelId: servers[1].videoChannel.id
281 playlistServer2Id1 = res.body.videoPlaylist.id
285 const res = await createVideoPlaylist({
287 token: servers[1].accessToken,
289 displayName: 'playlist 3',
290 privacy: VideoPlaylistPrivacy.PUBLIC,
291 thumbnailfile: 'thumbnail.jpg',
292 videoChannelId: servers[1].videoChannel.id
296 playlistServer2Id2 = res.body.videoPlaylist.id
297 playlistServer2UUID2 = res.body.videoPlaylist.uuid
300 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
301 await addVideoInPlaylist({
303 token: servers[1].accessToken,
305 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
307 await addVideoInPlaylist({
309 token: servers[1].accessToken,
311 elementAttrs: { videoId: servers[1].videos[1].id }
315 await waitJobs(servers)
318 for (const server of [ servers[0], servers[1] ]) {
319 const res = await getVideoPlaylistsList(server.url, 0, 5)
321 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
322 expect(playlist2).to.not.be.undefined
323 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
325 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
326 expect(playlist3).to.not.be.undefined
327 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
330 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
331 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
332 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
335 it('Should have the playlist on server 3 after a new follow', async function () {
338 // Server 2 and server 3 follow each other
339 await doubleFollow(servers[1], servers[2])
341 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
343 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
344 expect(playlist2).to.not.be.undefined
345 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
347 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
351 describe('List playlists', function () {
353 it('Should correctly list the playlists', async function () {
357 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt')
359 expect(res.body.total).to.equal(3)
361 const data: VideoPlaylist[] = res.body.data
362 expect(data).to.have.lengthOf(2)
363 expect(data[0].displayName).to.equal('playlist 2')
364 expect(data[1].displayName).to.equal('playlist 3')
368 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt')
370 expect(res.body.total).to.equal(3)
372 const data: VideoPlaylist[] = res.body.data
373 expect(data).to.have.lengthOf(2)
374 expect(data[0].displayName).to.equal('playlist 2')
375 expect(data[1].displayName).to.equal('my super playlist')
379 it('Should list video channel playlists', async function () {
383 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt')
385 expect(res.body.total).to.equal(1)
387 const data: VideoPlaylist[] = res.body.data
388 expect(data).to.have.lengthOf(1)
389 expect(data[0].displayName).to.equal('my super playlist')
393 it('Should list account playlists', async function () {
397 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt')
399 expect(res.body.total).to.equal(2)
401 const data: VideoPlaylist[] = res.body.data
402 expect(data).to.have.lengthOf(1)
403 expect(data[0].displayName).to.equal('playlist 2')
407 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt')
409 expect(res.body.total).to.equal(2)
411 const data: VideoPlaylist[] = res.body.data
412 expect(data).to.have.lengthOf(1)
413 expect(data[0].displayName).to.equal('playlist 3')
417 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3')
419 expect(res.body.total).to.equal(1)
421 const data: VideoPlaylist[] = res.body.data
422 expect(data).to.have.lengthOf(1)
423 expect(data[0].displayName).to.equal('playlist 3')
427 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4')
429 expect(res.body.total).to.equal(0)
431 const data: VideoPlaylist[] = res.body.data
432 expect(data).to.have.lengthOf(0)
436 it('Should not list unlisted or private playlists', async function () {
439 await createVideoPlaylist({
441 token: servers[1].accessToken,
443 displayName: 'playlist unlisted',
444 privacy: VideoPlaylistPrivacy.UNLISTED
448 await createVideoPlaylist({
450 token: servers[1].accessToken,
452 displayName: 'playlist private',
453 privacy: VideoPlaylistPrivacy.PRIVATE
457 await waitJobs(servers)
460 for (const server of servers) {
462 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
463 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
466 expect(results[0].body.total).to.equal(2)
467 expect(results[1].body.total).to.equal(3)
469 for (const res of results) {
470 const data: VideoPlaylist[] = res.body.data
471 expect(data).to.have.lengthOf(2)
472 expect(data[0].displayName).to.equal('playlist 3')
473 expect(data[1].displayName).to.equal('playlist 2')
479 describe('Update playlists', function () {
481 it('Should update a playlist', async function () {
484 await updateVideoPlaylist({
486 token: servers[1].accessToken,
488 displayName: 'playlist 3 updated',
489 description: 'description updated',
490 privacy: VideoPlaylistPrivacy.UNLISTED,
491 thumbnailfile: 'thumbnail.jpg',
492 videoChannelId: servers[1].videoChannel.id
494 playlistId: playlistServer2Id2
497 await waitJobs(servers)
499 for (const server of servers) {
500 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
501 const playlist: VideoPlaylist = res.body
503 expect(playlist.displayName).to.equal('playlist 3 updated')
504 expect(playlist.description).to.equal('description updated')
506 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
507 expect(playlist.privacy.label).to.equal('Unlisted')
509 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
510 expect(playlist.type.label).to.equal('Regular')
512 expect(playlist.videosLength).to.equal(2)
514 expect(playlist.ownerAccount.name).to.equal('root')
515 expect(playlist.ownerAccount.displayName).to.equal('root')
516 expect(playlist.videoChannel.name).to.equal('root_channel')
517 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
522 describe('Element timestamps', function () {
524 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
527 const addVideo = (elementAttrs: any) => {
528 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
531 const res = await createVideoPlaylist({
533 token: servers[0].accessToken,
535 displayName: 'playlist 4',
536 privacy: VideoPlaylistPrivacy.PUBLIC,
537 videoChannelId: servers[0].videoChannel.id
541 playlistServer1Id = res.body.videoPlaylist.id
542 playlistServer1UUID = res.body.videoPlaylist.uuid
544 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
545 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
546 await addVideo({ videoId: servers[2].videos[2].uuid })
548 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
549 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
553 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
554 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
558 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
559 playlistElementNSFW = res.body.videoPlaylistElement.id
561 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
562 await addVideo({ videoId: nsfwVideoServer1 })
565 await waitJobs(servers)
568 it('Should correctly list playlist videos', async function () {
571 for (const server of servers) {
572 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
574 expect(res.body.total).to.equal(8)
576 const videoElements: VideoPlaylistElement[] = res.body.data
577 expect(videoElements).to.have.lengthOf(8)
579 expect(videoElements[0].video.name).to.equal('video 0 server 1')
580 expect(videoElements[0].position).to.equal(1)
581 expect(videoElements[0].startTimestamp).to.equal(15)
582 expect(videoElements[0].stopTimestamp).to.equal(28)
584 expect(videoElements[1].video.name).to.equal('video 1 server 3')
585 expect(videoElements[1].position).to.equal(2)
586 expect(videoElements[1].startTimestamp).to.equal(35)
587 expect(videoElements[1].stopTimestamp).to.be.null
589 expect(videoElements[2].video.name).to.equal('video 2 server 3')
590 expect(videoElements[2].position).to.equal(3)
591 expect(videoElements[2].startTimestamp).to.be.null
592 expect(videoElements[2].stopTimestamp).to.be.null
594 expect(videoElements[3].video.name).to.equal('video 3 server 1')
595 expect(videoElements[3].position).to.equal(4)
596 expect(videoElements[3].startTimestamp).to.be.null
597 expect(videoElements[3].stopTimestamp).to.equal(35)
599 expect(videoElements[4].video.name).to.equal('video 4 server 1')
600 expect(videoElements[4].position).to.equal(5)
601 expect(videoElements[4].startTimestamp).to.equal(45)
602 expect(videoElements[4].stopTimestamp).to.equal(60)
604 expect(videoElements[5].video.name).to.equal('NSFW video')
605 expect(videoElements[5].position).to.equal(6)
606 expect(videoElements[5].startTimestamp).to.equal(5)
607 expect(videoElements[5].stopTimestamp).to.be.null
609 expect(videoElements[6].video.name).to.equal('NSFW video')
610 expect(videoElements[6].position).to.equal(7)
611 expect(videoElements[6].startTimestamp).to.equal(4)
612 expect(videoElements[6].stopTimestamp).to.be.null
614 expect(videoElements[7].video.name).to.equal('NSFW video')
615 expect(videoElements[7].position).to.equal(8)
616 expect(videoElements[7].startTimestamp).to.be.null
617 expect(videoElements[7].stopTimestamp).to.be.null
619 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
620 expect(res3.body.data).to.have.lengthOf(2)
625 describe('Element type', function () {
626 let groupUser1: ServerInfo[]
627 let groupWithoutToken1: ServerInfo[]
628 let group1: ServerInfo[]
629 let group2: ServerInfo[]
635 before(async function () {
638 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
639 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
640 group1 = [ servers[0] ]
641 group2 = [ servers[1], servers[2] ]
643 const res = await createVideoPlaylist({
645 token: userAccessTokenServer1,
647 displayName: 'playlist 56',
648 privacy: VideoPlaylistPrivacy.PUBLIC,
649 videoChannelId: servers[0].videoChannel.id
653 const playlistServer1Id2 = res.body.videoPlaylist.id
654 playlistServer1UUID2 = res.body.videoPlaylist.uuid
656 const addVideo = (elementAttrs: any) => {
657 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
660 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
661 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
662 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
664 await waitJobs(servers)
666 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
667 await addVideo({ videoId: video2, startTimestamp: 35 })
668 await addVideo({ videoId: video3 })
670 await waitJobs(servers)
673 it('Should update the element type if the video is private', async function () {
676 const name = 'video 89'
680 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
681 await waitJobs(servers)
683 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
684 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
685 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
686 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
690 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
691 await waitJobs(servers)
693 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
694 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
695 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
696 // We deleted the video, so even if we recreated it, the old entry is still deleted
697 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
701 it('Should update the element type if the video is blacklisted', async function () {
704 const name = 'video 89'
708 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
709 await waitJobs(servers)
711 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
712 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
713 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
714 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
718 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
719 await waitJobs(servers)
721 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
722 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
723 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
724 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
725 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
729 it('Should update the element type if the account or server of the video is blocked', async function () {
732 const name = 'video 90'
736 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
737 await waitJobs(servers)
739 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
740 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
742 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
743 await waitJobs(servers)
745 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
749 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
750 await waitJobs(servers)
752 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
753 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
755 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
756 await waitJobs(servers)
758 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
762 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
763 await waitJobs(servers)
765 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
766 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
768 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
769 await waitJobs(servers)
771 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
775 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
776 await waitJobs(servers)
778 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
779 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
781 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
782 await waitJobs(servers)
784 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
788 it('Should hide the video if it is NSFW', async function () {
789 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
790 expect(res.body.total).to.equal(3)
792 const elements: VideoPlaylistElement[] = res.body.data
793 const element = elements.find(e => e.position === 3)
795 expect(element).to.exist
796 expect(element.video).to.be.null
797 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
802 describe('Managing playlist elements', function () {
804 it('Should reorder the playlist', async function () {
808 await reorderVideosPlaylist({
810 token: servers[0].accessToken,
811 playlistId: playlistServer1Id,
814 insertAfterPosition: 3
818 await waitJobs(servers)
820 for (const server of servers) {
821 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
822 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
824 expect(names).to.deep.equal([
838 await reorderVideosPlaylist({
840 token: servers[0].accessToken,
841 playlistId: playlistServer1Id,
845 insertAfterPosition: 4
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,
875 insertAfterPosition: 3
879 await waitJobs(servers)
881 for (const server of servers) {
882 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
883 const elements: VideoPlaylistElement[] = res.body.data
884 const names = elements.map(v => v.video.name)
886 expect(names).to.deep.equal([
897 for (let i = 1; i <= elements.length; i++) {
898 expect(elements[i - 1].position).to.equal(i)
904 it('Should update startTimestamp/endTimestamp of some elements', async function () {
907 await updateVideoPlaylistElement({
909 token: servers[0].accessToken,
910 playlistId: playlistServer1Id,
911 playlistElementId: playlistElementServer1Video4,
917 await updateVideoPlaylistElement({
919 token: servers[0].accessToken,
920 playlistId: playlistServer1Id,
921 playlistElementId: playlistElementServer1Video5,
927 await waitJobs(servers)
929 for (const server of servers) {
930 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
931 const elements: VideoPlaylistElement[] = res.body.data
933 expect(elements[0].video.name).to.equal('video 3 server 1')
934 expect(elements[0].position).to.equal(1)
935 expect(elements[0].startTimestamp).to.equal(1)
936 expect(elements[0].stopTimestamp).to.equal(35)
938 expect(elements[5].video.name).to.equal('video 4 server 1')
939 expect(elements[5].position).to.equal(6)
940 expect(elements[5].startTimestamp).to.equal(45)
941 expect(elements[5].stopTimestamp).to.be.null
945 it('Should check videos existence in my playlist', async function () {
947 servers[0].videos[0].id,
949 servers[0].videos[3].id,
951 servers[0].videos[4].id
953 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
954 const obj = res.body as VideoExistInPlaylist
957 const elem = obj[servers[0].videos[0].id]
958 expect(elem).to.have.lengthOf(1)
959 expect(elem[0].playlistElementId).to.exist
960 expect(elem[0].playlistId).to.equal(playlistServer1Id)
961 expect(elem[0].startTimestamp).to.equal(15)
962 expect(elem[0].stopTimestamp).to.equal(28)
966 const elem = obj[servers[0].videos[3].id]
967 expect(elem).to.have.lengthOf(1)
968 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
969 expect(elem[0].playlistId).to.equal(playlistServer1Id)
970 expect(elem[0].startTimestamp).to.equal(1)
971 expect(elem[0].stopTimestamp).to.equal(35)
975 const elem = obj[servers[0].videos[4].id]
976 expect(elem).to.have.lengthOf(1)
977 expect(elem[0].playlistId).to.equal(playlistServer1Id)
978 expect(elem[0].startTimestamp).to.equal(45)
979 expect(elem[0].stopTimestamp).to.equal(null)
982 expect(obj[42000]).to.have.lengthOf(0)
983 expect(obj[43000]).to.have.lengthOf(0)
986 it('Should automatically update updatedAt field of playlists', async function () {
987 const server = servers[1]
988 const videoId = servers[1].videos[5].id
990 async function getPlaylistNames () {
991 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
993 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
996 const elementAttrs = { videoId }
997 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
998 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
1000 const element1 = res1.body.videoPlaylistElement.id
1001 const element2 = res2.body.videoPlaylistElement.id
1003 const names1 = await getPlaylistNames()
1004 expect(names1[0]).to.equal('playlist 3 updated')
1005 expect(names1[1]).to.equal('playlist 2')
1007 await removeVideoFromPlaylist({
1009 token: server.accessToken,
1010 playlistId: playlistServer2Id1,
1011 playlistElementId: element1
1014 const names2 = await getPlaylistNames()
1015 expect(names2[0]).to.equal('playlist 2')
1016 expect(names2[1]).to.equal('playlist 3 updated')
1018 await removeVideoFromPlaylist({
1020 token: server.accessToken,
1021 playlistId: playlistServer2Id2,
1022 playlistElementId: element2
1025 const names3 = await getPlaylistNames()
1026 expect(names3[0]).to.equal('playlist 3 updated')
1027 expect(names3[1]).to.equal('playlist 2')
1030 it('Should delete some elements', async function () {
1033 await removeVideoFromPlaylist({
1034 url: servers[0].url,
1035 token: servers[0].accessToken,
1036 playlistId: playlistServer1Id,
1037 playlistElementId: playlistElementServer1Video4
1040 await removeVideoFromPlaylist({
1041 url: servers[0].url,
1042 token: servers[0].accessToken,
1043 playlistId: playlistServer1Id,
1044 playlistElementId: playlistElementNSFW
1047 await waitJobs(servers)
1049 for (const server of servers) {
1050 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1052 expect(res.body.total).to.equal(6)
1054 const elements: VideoPlaylistElement[] = res.body.data
1055 expect(elements).to.have.lengthOf(6)
1057 expect(elements[0].video.name).to.equal('video 0 server 1')
1058 expect(elements[0].position).to.equal(1)
1060 expect(elements[1].video.name).to.equal('video 2 server 3')
1061 expect(elements[1].position).to.equal(2)
1063 expect(elements[2].video.name).to.equal('video 1 server 3')
1064 expect(elements[2].position).to.equal(3)
1066 expect(elements[3].video.name).to.equal('video 4 server 1')
1067 expect(elements[3].position).to.equal(4)
1069 expect(elements[4].video.name).to.equal('NSFW video')
1070 expect(elements[4].position).to.equal(5)
1072 expect(elements[5].video.name).to.equal('NSFW video')
1073 expect(elements[5].position).to.equal(6)
1077 it('Should be able to create a public playlist, and set it to private', async function () {
1080 const res = await createVideoPlaylist({
1081 url: servers[0].url,
1082 token: servers[0].accessToken,
1084 displayName: 'my super public playlist',
1085 privacy: VideoPlaylistPrivacy.PUBLIC,
1086 videoChannelId: servers[0].videoChannel.id
1089 const videoPlaylistIds = res.body.videoPlaylist
1091 await waitJobs(servers)
1093 for (const server of servers) {
1094 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 200)
1097 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1098 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1100 await waitJobs(servers)
1102 for (const server of [ servers[1], servers[2] ]) {
1103 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 404)
1105 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, 401)
1107 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, 200)
1111 describe('Playlist deletion', function () {
1113 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1116 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1118 await waitJobs(servers)
1120 for (const server of servers) {
1121 await getVideoPlaylist(server.url, playlistServer1UUID, 404)
1125 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1128 for (const server of servers) {
1129 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1133 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1136 const finder = data => data.find(p => p.displayName === 'my super playlist')
1139 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1140 expect(res.body.total).to.equal(3)
1141 expect(finder(res.body.data)).to.not.be.undefined
1144 await unfollow(servers[2].url, servers[2].accessToken, servers[0])
1147 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1148 expect(res.body.total).to.equal(1)
1150 expect(finder(res.body.data)).to.be.undefined
1154 it('Should delete a channel and put the associated playlist in private mode', async function () {
1157 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1158 const videoChannelId = res.body.videoChannel.id
1160 const res2 = await createVideoPlaylist({
1161 url: servers[0].url,
1162 token: servers[0].accessToken,
1164 displayName: 'channel playlist',
1165 privacy: VideoPlaylistPrivacy.PUBLIC,
1169 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1171 await waitJobs(servers)
1173 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1175 await waitJobs(servers)
1177 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1178 expect(res3.body.displayName).to.equal('channel playlist')
1179 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1181 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, 404)
1184 it('Should delete an account and delete its playlists', async function () {
1187 const user = { username: 'user_1', password: 'password' }
1188 const res = await createUser({
1189 url: servers[0].url,
1190 accessToken: servers[0].accessToken,
1191 username: user.username,
1192 password: user.password
1195 const userId = res.body.user.id
1196 const userAccessToken = await userLogin(servers[0], user)
1198 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1199 const userChannel = (resChannel.body as User).videoChannels[0]
1201 await createVideoPlaylist({
1202 url: servers[0].url,
1203 token: userAccessToken,
1205 displayName: 'playlist to be deleted',
1206 privacy: VideoPlaylistPrivacy.PUBLIC,
1207 videoChannelId: userChannel.id
1211 await waitJobs(servers)
1213 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1216 for (const server of [ servers[0], servers[1] ]) {
1217 const res = await getVideoPlaylistsList(server.url, 0, 15)
1218 expect(finder(res.body.data)).to.not.be.undefined
1222 await removeUser(servers[0].url, userId, servers[0].accessToken)
1223 await waitJobs(servers)
1226 for (const server of [ servers[0], servers[1] ]) {
1227 const res = await getVideoPlaylistsList(server.url, 0, 15)
1228 expect(finder(res.body.data)).to.be.undefined
1234 after(async function () {
1235 await cleanupTests(servers)