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/miscs/http-error-codes'
10 checkPlaylistFilesWereRemoved,
17 doVideosExistInMyPlaylist,
18 flushAndRunMultipleServers,
19 generateUserAccessToken,
21 getAccountPlaylistsList,
22 getAccountPlaylistsListWithToken,
25 getVideoChannelPlaylistsList,
27 getVideoPlaylistPrivacies,
28 getVideoPlaylistsList,
29 getVideoPlaylistWithToken,
31 removeVideoFromBlacklist,
32 removeVideoFromPlaylist,
33 reorderVideosPlaylist,
35 setAccessTokensToServers,
36 setDefaultVideoChannel,
41 updateVideoPlaylistElement,
47 } from '../../../../shared/extra-utils'
49 addAccountToAccountBlocklist,
50 addAccountToServerBlocklist,
51 addServerToAccountBlocklist,
52 addServerToServerBlocklist,
53 removeAccountFromAccountBlocklist,
54 removeAccountFromServerBlocklist,
55 removeServerFromAccountBlocklist,
56 removeServerFromServerBlocklist
57 } from '../../../../shared/extra-utils/users/blocklist'
58 import { User } from '../../../../shared/models/users'
59 import { VideoPlaylistCreateResult, VideoPrivacy } from '../../../../shared/models/videos'
60 import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
61 import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
62 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
63 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
64 import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
66 const expect = chai.expect
68 async function checkPlaylistElementType (
69 servers: ServerInfo[],
71 type: VideoPlaylistElementType,
76 for (const server of servers) {
77 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10)
78 expect(res.body.total).to.equal(total)
80 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position)
81 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
83 if (type === VideoPlaylistElementType.REGULAR) {
84 expect(videoElement.video).to.not.be.null
85 expect(videoElement.video.name).to.equal(name)
87 expect(videoElement.video).to.be.null
92 describe('Test video playlists', function () {
93 let servers: ServerInfo[] = []
95 let playlistServer2Id1: number
96 let playlistServer2Id2: number
97 let playlistServer2UUID2: number
99 let playlistServer1Id: number
100 let playlistServer1UUID: string
101 let playlistServer1UUID2: string
103 let playlistElementServer1Video4: number
104 let playlistElementServer1Video5: number
105 let playlistElementNSFW: number
107 let nsfwVideoServer1: number
109 let userAccessTokenServer1: string
111 before(async function () {
114 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
116 // Get the access tokens
117 await setAccessTokensToServers(servers)
118 await setDefaultVideoChannel(servers)
120 // Server 1 and server 2 follow each other
121 await doubleFollow(servers[0], servers[1])
122 // Server 1 and server 3 follow each other
123 await doubleFollow(servers[0], servers[2])
126 servers[0].videos = []
127 servers[1].videos = []
128 servers[2].videos = []
130 for (const server of servers) {
131 for (let i = 0; i < 7; i++) {
132 const name = `video ${i} server ${server.serverNumber}`
133 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
135 server.videos.push(resVideo.body.video)
140 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
145 accessToken: servers[0].accessToken,
149 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
152 await waitJobs(servers)
155 describe('Get default playlists', function () {
156 it('Should list video playlist privacies', async function () {
157 const res = await getVideoPlaylistPrivacies(servers[0].url)
159 const privacies = res.body
160 expect(Object.keys(privacies)).to.have.length.at.least(3)
162 expect(privacies[3]).to.equal('Private')
165 it('Should list watch later playlist', async function () {
166 const url = servers[0].url
167 const accessToken = servers[0].accessToken
170 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
172 expect(res.body.total).to.equal(1)
173 expect(res.body.data).to.have.lengthOf(1)
175 const playlist: VideoPlaylist = res.body.data[0]
176 expect(playlist.displayName).to.equal('Watch later')
177 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
178 expect(playlist.type.label).to.equal('Watch later')
182 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
184 expect(res.body.total).to.equal(0)
185 expect(res.body.data).to.have.lengthOf(0)
189 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
190 expect(res.body.total).to.equal(0)
191 expect(res.body.data).to.have.lengthOf(0)
195 it('Should get private playlist for a classic user', async function () {
196 const token = await generateUserAccessToken(servers[0], 'toto')
198 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
200 expect(res.body.total).to.equal(1)
201 expect(res.body.data).to.have.lengthOf(1)
203 const playlistId = res.body.data[0].id
204 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
208 describe('Create and federate playlists', function () {
210 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
213 await createVideoPlaylist({
215 token: servers[0].accessToken,
217 displayName: 'my super playlist',
218 privacy: VideoPlaylistPrivacy.PUBLIC,
219 description: 'my super description',
220 thumbnailfile: 'thumbnail.jpg',
221 videoChannelId: servers[0].videoChannel.id
225 await waitJobs(servers)
226 // Processing a playlist by the receiver could be long
229 for (const server of servers) {
230 const res = await getVideoPlaylistsList(server.url, 0, 5)
231 expect(res.body.total).to.equal(1)
232 expect(res.body.data).to.have.lengthOf(1)
234 const playlistFromList = res.body.data[0] as VideoPlaylist
236 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
237 const playlistFromGet = res2.body as VideoPlaylist
239 for (const playlist of [ playlistFromGet, playlistFromList ]) {
240 expect(playlist.id).to.be.a('number')
241 expect(playlist.uuid).to.be.a('string')
243 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
245 expect(playlist.displayName).to.equal('my super playlist')
246 expect(playlist.description).to.equal('my super description')
247 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
248 expect(playlist.privacy.label).to.equal('Public')
249 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
250 expect(playlist.type.label).to.equal('Regular')
251 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
253 expect(playlist.videosLength).to.equal(0)
255 expect(playlist.ownerAccount.name).to.equal('root')
256 expect(playlist.ownerAccount.displayName).to.equal('root')
257 expect(playlist.videoChannel.name).to.equal('root_channel')
258 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
263 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
267 const res = await createVideoPlaylist({
269 token: servers[1].accessToken,
271 displayName: 'playlist 2',
272 privacy: VideoPlaylistPrivacy.PUBLIC,
273 videoChannelId: servers[1].videoChannel.id
276 playlistServer2Id1 = res.body.videoPlaylist.id
280 const res = await createVideoPlaylist({
282 token: servers[1].accessToken,
284 displayName: 'playlist 3',
285 privacy: VideoPlaylistPrivacy.PUBLIC,
286 thumbnailfile: 'thumbnail.jpg',
287 videoChannelId: servers[1].videoChannel.id
291 playlistServer2Id2 = res.body.videoPlaylist.id
292 playlistServer2UUID2 = res.body.videoPlaylist.uuid
295 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
296 await addVideoInPlaylist({
298 token: servers[1].accessToken,
300 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
302 await addVideoInPlaylist({
304 token: servers[1].accessToken,
306 elementAttrs: { videoId: servers[1].videos[1].id }
310 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)
432 describe('Playlist rights', function () {
433 let unlistedPlaylist: VideoPlaylistCreateResult
434 let privatePlaylist: VideoPlaylistCreateResult
436 before(async function () {
440 const res = await createVideoPlaylist({
442 token: servers[1].accessToken,
444 displayName: 'playlist unlisted',
445 privacy: VideoPlaylistPrivacy.UNLISTED,
446 videoChannelId: servers[1].videoChannel.id
449 unlistedPlaylist = res.body.videoPlaylist
453 const res = await createVideoPlaylist({
455 token: servers[1].accessToken,
457 displayName: 'playlist private',
458 privacy: VideoPlaylistPrivacy.PRIVATE
461 privatePlaylist = res.body.videoPlaylist
464 await waitJobs(servers)
468 it('Should not list unlisted or private playlists', async function () {
469 for (const server of servers) {
471 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
472 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
475 expect(results[0].body.total).to.equal(2)
476 expect(results[1].body.total).to.equal(3)
478 for (const res of results) {
479 const data: VideoPlaylist[] = res.body.data
480 expect(data).to.have.lengthOf(2)
481 expect(data[0].displayName).to.equal('playlist 3')
482 expect(data[1].displayName).to.equal('playlist 2')
487 it('Should not get unlisted playlist using only the id', async function () {
488 await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404)
491 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
492 await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid)
493 await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID)
496 it('Should not get private playlist without token', async function () {
497 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
498 await getVideoPlaylist(servers[1].url, id, 401)
502 it('Should get private playlist with a token', async function () {
503 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
504 await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id)
509 describe('Update playlists', function () {
511 it('Should update a playlist', async function () {
514 await updateVideoPlaylist({
516 token: servers[1].accessToken,
518 displayName: 'playlist 3 updated',
519 description: 'description updated',
520 privacy: VideoPlaylistPrivacy.UNLISTED,
521 thumbnailfile: 'thumbnail.jpg',
522 videoChannelId: servers[1].videoChannel.id
524 playlistId: playlistServer2Id2
527 await waitJobs(servers)
529 for (const server of servers) {
530 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
531 const playlist: VideoPlaylist = res.body
533 expect(playlist.displayName).to.equal('playlist 3 updated')
534 expect(playlist.description).to.equal('description updated')
536 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
537 expect(playlist.privacy.label).to.equal('Unlisted')
539 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
540 expect(playlist.type.label).to.equal('Regular')
542 expect(playlist.videosLength).to.equal(2)
544 expect(playlist.ownerAccount.name).to.equal('root')
545 expect(playlist.ownerAccount.displayName).to.equal('root')
546 expect(playlist.videoChannel.name).to.equal('root_channel')
547 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
552 describe('Element timestamps', function () {
554 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
557 const addVideo = (elementAttrs: any) => {
558 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
561 const res = await createVideoPlaylist({
563 token: servers[0].accessToken,
565 displayName: 'playlist 4',
566 privacy: VideoPlaylistPrivacy.PUBLIC,
567 videoChannelId: servers[0].videoChannel.id
571 playlistServer1Id = res.body.videoPlaylist.id
572 playlistServer1UUID = res.body.videoPlaylist.uuid
574 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
575 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
576 await addVideo({ videoId: servers[2].videos[2].uuid })
578 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
579 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
583 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
584 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
588 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
589 playlistElementNSFW = res.body.videoPlaylistElement.id
591 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
592 await addVideo({ videoId: nsfwVideoServer1 })
595 await waitJobs(servers)
598 it('Should correctly list playlist videos', async function () {
601 for (const server of servers) {
602 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
604 expect(res.body.total).to.equal(8)
606 const videoElements: VideoPlaylistElement[] = res.body.data
607 expect(videoElements).to.have.lengthOf(8)
609 expect(videoElements[0].video.name).to.equal('video 0 server 1')
610 expect(videoElements[0].position).to.equal(1)
611 expect(videoElements[0].startTimestamp).to.equal(15)
612 expect(videoElements[0].stopTimestamp).to.equal(28)
614 expect(videoElements[1].video.name).to.equal('video 1 server 3')
615 expect(videoElements[1].position).to.equal(2)
616 expect(videoElements[1].startTimestamp).to.equal(35)
617 expect(videoElements[1].stopTimestamp).to.be.null
619 expect(videoElements[2].video.name).to.equal('video 2 server 3')
620 expect(videoElements[2].position).to.equal(3)
621 expect(videoElements[2].startTimestamp).to.be.null
622 expect(videoElements[2].stopTimestamp).to.be.null
624 expect(videoElements[3].video.name).to.equal('video 3 server 1')
625 expect(videoElements[3].position).to.equal(4)
626 expect(videoElements[3].startTimestamp).to.be.null
627 expect(videoElements[3].stopTimestamp).to.equal(35)
629 expect(videoElements[4].video.name).to.equal('video 4 server 1')
630 expect(videoElements[4].position).to.equal(5)
631 expect(videoElements[4].startTimestamp).to.equal(45)
632 expect(videoElements[4].stopTimestamp).to.equal(60)
634 expect(videoElements[5].video.name).to.equal('NSFW video')
635 expect(videoElements[5].position).to.equal(6)
636 expect(videoElements[5].startTimestamp).to.equal(5)
637 expect(videoElements[5].stopTimestamp).to.be.null
639 expect(videoElements[6].video.name).to.equal('NSFW video')
640 expect(videoElements[6].position).to.equal(7)
641 expect(videoElements[6].startTimestamp).to.equal(4)
642 expect(videoElements[6].stopTimestamp).to.be.null
644 expect(videoElements[7].video.name).to.equal('NSFW video')
645 expect(videoElements[7].position).to.equal(8)
646 expect(videoElements[7].startTimestamp).to.be.null
647 expect(videoElements[7].stopTimestamp).to.be.null
649 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
650 expect(res3.body.data).to.have.lengthOf(2)
655 describe('Element type', function () {
656 let groupUser1: ServerInfo[]
657 let groupWithoutToken1: ServerInfo[]
658 let group1: ServerInfo[]
659 let group2: ServerInfo[]
665 before(async function () {
668 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
669 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
670 group1 = [ servers[0] ]
671 group2 = [ servers[1], servers[2] ]
673 const res = await createVideoPlaylist({
675 token: userAccessTokenServer1,
677 displayName: 'playlist 56',
678 privacy: VideoPlaylistPrivacy.PUBLIC,
679 videoChannelId: servers[0].videoChannel.id
683 const playlistServer1Id2 = res.body.videoPlaylist.id
684 playlistServer1UUID2 = res.body.videoPlaylist.uuid
686 const addVideo = (elementAttrs: any) => {
687 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
690 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
691 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
692 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
694 await waitJobs(servers)
696 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
697 await addVideo({ videoId: video2, startTimestamp: 35 })
698 await addVideo({ videoId: video3 })
700 await waitJobs(servers)
703 it('Should update the element type if the video is private', async function () {
706 const name = 'video 89'
710 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
711 await waitJobs(servers)
713 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
714 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
715 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
716 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
720 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
721 await waitJobs(servers)
723 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
724 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
725 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
726 // We deleted the video, so even if we recreated it, the old entry is still deleted
727 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
731 it('Should update the element type if the video is blacklisted', async function () {
734 const name = 'video 89'
738 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
739 await waitJobs(servers)
741 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
742 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
743 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
744 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
748 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
749 await waitJobs(servers)
751 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
752 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
753 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
754 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
755 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
759 it('Should update the element type if the account or server of the video is blocked', async function () {
762 const name = 'video 90'
766 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
767 await waitJobs(servers)
769 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
770 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
772 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
773 await waitJobs(servers)
775 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
779 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
780 await waitJobs(servers)
782 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
783 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
785 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
786 await waitJobs(servers)
788 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
792 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
793 await waitJobs(servers)
795 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
796 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
798 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
799 await waitJobs(servers)
801 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
805 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
806 await waitJobs(servers)
808 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
809 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
811 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
812 await waitJobs(servers)
814 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
818 it('Should hide the video if it is NSFW', async function () {
819 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
820 expect(res.body.total).to.equal(3)
822 const elements: VideoPlaylistElement[] = res.body.data
823 const element = elements.find(e => e.position === 3)
825 expect(element).to.exist
826 expect(element.video).to.be.null
827 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
832 describe('Managing playlist elements', function () {
834 it('Should reorder the playlist', async function () {
838 await reorderVideosPlaylist({
840 token: servers[0].accessToken,
841 playlistId: playlistServer1Id,
844 insertAfterPosition: 3
848 await waitJobs(servers)
850 for (const server of servers) {
851 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
852 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
854 expect(names).to.deep.equal([
868 await reorderVideosPlaylist({
870 token: servers[0].accessToken,
871 playlistId: playlistServer1Id,
875 insertAfterPosition: 4
879 await waitJobs(servers)
881 for (const server of servers) {
882 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
883 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
885 expect(names).to.deep.equal([
899 await reorderVideosPlaylist({
901 token: servers[0].accessToken,
902 playlistId: playlistServer1Id,
905 insertAfterPosition: 3
909 await waitJobs(servers)
911 for (const server of servers) {
912 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
913 const elements: VideoPlaylistElement[] = res.body.data
914 const names = elements.map(v => v.video.name)
916 expect(names).to.deep.equal([
927 for (let i = 1; i <= elements.length; i++) {
928 expect(elements[i - 1].position).to.equal(i)
934 it('Should update startTimestamp/endTimestamp of some elements', async function () {
937 await updateVideoPlaylistElement({
939 token: servers[0].accessToken,
940 playlistId: playlistServer1Id,
941 playlistElementId: playlistElementServer1Video4,
947 await updateVideoPlaylistElement({
949 token: servers[0].accessToken,
950 playlistId: playlistServer1Id,
951 playlistElementId: playlistElementServer1Video5,
957 await waitJobs(servers)
959 for (const server of servers) {
960 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
961 const elements: VideoPlaylistElement[] = res.body.data
963 expect(elements[0].video.name).to.equal('video 3 server 1')
964 expect(elements[0].position).to.equal(1)
965 expect(elements[0].startTimestamp).to.equal(1)
966 expect(elements[0].stopTimestamp).to.equal(35)
968 expect(elements[5].video.name).to.equal('video 4 server 1')
969 expect(elements[5].position).to.equal(6)
970 expect(elements[5].startTimestamp).to.equal(45)
971 expect(elements[5].stopTimestamp).to.be.null
975 it('Should check videos existence in my playlist', async function () {
977 servers[0].videos[0].id,
979 servers[0].videos[3].id,
981 servers[0].videos[4].id
983 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
984 const obj = res.body as VideoExistInPlaylist
987 const elem = obj[servers[0].videos[0].id]
988 expect(elem).to.have.lengthOf(1)
989 expect(elem[0].playlistElementId).to.exist
990 expect(elem[0].playlistId).to.equal(playlistServer1Id)
991 expect(elem[0].startTimestamp).to.equal(15)
992 expect(elem[0].stopTimestamp).to.equal(28)
996 const elem = obj[servers[0].videos[3].id]
997 expect(elem).to.have.lengthOf(1)
998 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
999 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1000 expect(elem[0].startTimestamp).to.equal(1)
1001 expect(elem[0].stopTimestamp).to.equal(35)
1005 const elem = obj[servers[0].videos[4].id]
1006 expect(elem).to.have.lengthOf(1)
1007 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1008 expect(elem[0].startTimestamp).to.equal(45)
1009 expect(elem[0].stopTimestamp).to.equal(null)
1012 expect(obj[42000]).to.have.lengthOf(0)
1013 expect(obj[43000]).to.have.lengthOf(0)
1016 it('Should automatically update updatedAt field of playlists', async function () {
1017 const server = servers[1]
1018 const videoId = servers[1].videos[5].id
1020 async function getPlaylistNames () {
1021 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
1023 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
1026 const elementAttrs = { videoId }
1027 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
1028 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
1030 const element1 = res1.body.videoPlaylistElement.id
1031 const element2 = res2.body.videoPlaylistElement.id
1033 const names1 = await getPlaylistNames()
1034 expect(names1[0]).to.equal('playlist 3 updated')
1035 expect(names1[1]).to.equal('playlist 2')
1037 await removeVideoFromPlaylist({
1039 token: server.accessToken,
1040 playlistId: playlistServer2Id1,
1041 playlistElementId: element1
1044 const names2 = await getPlaylistNames()
1045 expect(names2[0]).to.equal('playlist 2')
1046 expect(names2[1]).to.equal('playlist 3 updated')
1048 await removeVideoFromPlaylist({
1050 token: server.accessToken,
1051 playlistId: playlistServer2Id2,
1052 playlistElementId: element2
1055 const names3 = await getPlaylistNames()
1056 expect(names3[0]).to.equal('playlist 3 updated')
1057 expect(names3[1]).to.equal('playlist 2')
1060 it('Should delete some elements', async function () {
1063 await removeVideoFromPlaylist({
1064 url: servers[0].url,
1065 token: servers[0].accessToken,
1066 playlistId: playlistServer1Id,
1067 playlistElementId: playlistElementServer1Video4
1070 await removeVideoFromPlaylist({
1071 url: servers[0].url,
1072 token: servers[0].accessToken,
1073 playlistId: playlistServer1Id,
1074 playlistElementId: playlistElementNSFW
1077 await waitJobs(servers)
1079 for (const server of servers) {
1080 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1082 expect(res.body.total).to.equal(6)
1084 const elements: VideoPlaylistElement[] = res.body.data
1085 expect(elements).to.have.lengthOf(6)
1087 expect(elements[0].video.name).to.equal('video 0 server 1')
1088 expect(elements[0].position).to.equal(1)
1090 expect(elements[1].video.name).to.equal('video 2 server 3')
1091 expect(elements[1].position).to.equal(2)
1093 expect(elements[2].video.name).to.equal('video 1 server 3')
1094 expect(elements[2].position).to.equal(3)
1096 expect(elements[3].video.name).to.equal('video 4 server 1')
1097 expect(elements[3].position).to.equal(4)
1099 expect(elements[4].video.name).to.equal('NSFW video')
1100 expect(elements[4].position).to.equal(5)
1102 expect(elements[5].video.name).to.equal('NSFW video')
1103 expect(elements[5].position).to.equal(6)
1107 it('Should be able to create a public playlist, and set it to private', async function () {
1110 const res = await createVideoPlaylist({
1111 url: servers[0].url,
1112 token: servers[0].accessToken,
1114 displayName: 'my super public playlist',
1115 privacy: VideoPlaylistPrivacy.PUBLIC,
1116 videoChannelId: servers[0].videoChannel.id
1119 const videoPlaylistIds = res.body.videoPlaylist
1121 await waitJobs(servers)
1123 for (const server of servers) {
1124 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1127 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1128 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1130 await waitJobs(servers)
1132 for (const server of [ servers[1], servers[2] ]) {
1133 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404)
1135 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1137 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1141 describe('Playlist deletion', function () {
1143 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1146 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1148 await waitJobs(servers)
1150 for (const server of servers) {
1151 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404)
1155 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1158 for (const server of servers) {
1159 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1163 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1166 const finder = data => data.find(p => p.displayName === 'my super playlist')
1169 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1170 expect(res.body.total).to.equal(3)
1171 expect(finder(res.body.data)).to.not.be.undefined
1174 await unfollow(servers[2].url, servers[2].accessToken, servers[0])
1177 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1178 expect(res.body.total).to.equal(1)
1180 expect(finder(res.body.data)).to.be.undefined
1184 it('Should delete a channel and put the associated playlist in private mode', async function () {
1187 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1188 const videoChannelId = res.body.videoChannel.id
1190 const res2 = await createVideoPlaylist({
1191 url: servers[0].url,
1192 token: servers[0].accessToken,
1194 displayName: 'channel playlist',
1195 privacy: VideoPlaylistPrivacy.PUBLIC,
1199 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1201 await waitJobs(servers)
1203 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1205 await waitJobs(servers)
1207 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1208 expect(res3.body.displayName).to.equal('channel playlist')
1209 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1211 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404)
1214 it('Should delete an account and delete its playlists', async function () {
1217 const user = { username: 'user_1', password: 'password' }
1218 const res = await createUser({
1219 url: servers[0].url,
1220 accessToken: servers[0].accessToken,
1221 username: user.username,
1222 password: user.password
1225 const userId = res.body.user.id
1226 const userAccessToken = await userLogin(servers[0], user)
1228 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1229 const userChannel = (resChannel.body as User).videoChannels[0]
1231 await createVideoPlaylist({
1232 url: servers[0].url,
1233 token: userAccessToken,
1235 displayName: 'playlist to be deleted',
1236 privacy: VideoPlaylistPrivacy.PUBLIC,
1237 videoChannelId: userChannel.id
1241 await waitJobs(servers)
1243 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1246 for (const server of [ servers[0], servers[1] ]) {
1247 const res = await getVideoPlaylistsList(server.url, 0, 15)
1248 expect(finder(res.body.data)).to.not.be.undefined
1252 await removeUser(servers[0].url, userId, servers[0].accessToken)
1253 await waitJobs(servers)
1256 for (const server of [ servers[0], servers[1] ]) {
1257 const res = await getVideoPlaylistsList(server.url, 0, 15)
1258 expect(finder(res.body.data)).to.be.undefined
1264 after(async function () {
1265 await cleanupTests(servers)