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'
9 checkPlaylistFilesWereRemoved,
16 doVideosExistInMyPlaylist,
17 flushAndRunMultipleServers,
18 generateUserAccessToken,
20 getAccountPlaylistsList,
21 getAccountPlaylistsListWithToken,
24 getVideoChannelPlaylistsList,
26 getVideoPlaylistPrivacies,
27 getVideoPlaylistsList,
28 getVideoPlaylistWithToken,
30 removeVideoFromPlaylist,
31 reorderVideosPlaylist,
33 setAccessTokensToServers,
34 setDefaultVideoChannel,
38 updateVideoPlaylistElement,
44 } from '@shared/extra-utils'
49 VideoPlaylistCreateResult,
51 VideoPlaylistElementType,
55 } from '@shared/models'
57 const expect = chai.expect
59 async function checkPlaylistElementType (
60 servers: ServerInfo[],
62 type: VideoPlaylistElementType,
67 for (const server of servers) {
68 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10)
69 expect(res.body.total).to.equal(total)
71 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position)
72 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
74 if (type === VideoPlaylistElementType.REGULAR) {
75 expect(videoElement.video).to.not.be.null
76 expect(videoElement.video.name).to.equal(name)
78 expect(videoElement.video).to.be.null
83 describe('Test video playlists', function () {
84 let servers: ServerInfo[] = []
86 let playlistServer2Id1: number
87 let playlistServer2Id2: number
88 let playlistServer2UUID2: number
90 let playlistServer1Id: number
91 let playlistServer1UUID: string
92 let playlistServer1UUID2: string
94 let playlistElementServer1Video4: number
95 let playlistElementServer1Video5: number
96 let playlistElementNSFW: number
98 let nsfwVideoServer1: number
100 let userAccessTokenServer1: string
102 before(async function () {
105 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
107 // Get the access tokens
108 await setAccessTokensToServers(servers)
109 await setDefaultVideoChannel(servers)
111 // Server 1 and server 2 follow each other
112 await doubleFollow(servers[0], servers[1])
113 // Server 1 and server 3 follow each other
114 await doubleFollow(servers[0], servers[2])
117 servers[0].videos = []
118 servers[1].videos = []
119 servers[2].videos = []
121 for (const server of servers) {
122 for (let i = 0; i < 7; i++) {
123 const name = `video ${i} server ${server.serverNumber}`
124 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
126 server.videos.push(resVideo.body.video)
131 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
136 accessToken: servers[0].accessToken,
140 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
143 await waitJobs(servers)
146 describe('Get default playlists', function () {
147 it('Should list video playlist privacies', async function () {
148 const res = await getVideoPlaylistPrivacies(servers[0].url)
150 const privacies = res.body
151 expect(Object.keys(privacies)).to.have.length.at.least(3)
153 expect(privacies[3]).to.equal('Private')
156 it('Should list watch later playlist', async function () {
157 const url = servers[0].url
158 const accessToken = servers[0].accessToken
161 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
163 expect(res.body.total).to.equal(1)
164 expect(res.body.data).to.have.lengthOf(1)
166 const playlist: VideoPlaylist = res.body.data[0]
167 expect(playlist.displayName).to.equal('Watch later')
168 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
169 expect(playlist.type.label).to.equal('Watch later')
173 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
175 expect(res.body.total).to.equal(0)
176 expect(res.body.data).to.have.lengthOf(0)
180 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
181 expect(res.body.total).to.equal(0)
182 expect(res.body.data).to.have.lengthOf(0)
186 it('Should get private playlist for a classic user', async function () {
187 const token = await generateUserAccessToken(servers[0], 'toto')
189 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
191 expect(res.body.total).to.equal(1)
192 expect(res.body.data).to.have.lengthOf(1)
194 const playlistId = res.body.data[0].id
195 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
199 describe('Create and federate playlists', function () {
201 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
204 await createVideoPlaylist({
206 token: servers[0].accessToken,
208 displayName: 'my super playlist',
209 privacy: VideoPlaylistPrivacy.PUBLIC,
210 description: 'my super description',
211 thumbnailfile: 'thumbnail.jpg',
212 videoChannelId: servers[0].videoChannel.id
216 await waitJobs(servers)
217 // Processing a playlist by the receiver could be long
220 for (const server of servers) {
221 const res = await getVideoPlaylistsList(server.url, 0, 5)
222 expect(res.body.total).to.equal(1)
223 expect(res.body.data).to.have.lengthOf(1)
225 const playlistFromList = res.body.data[0] as VideoPlaylist
227 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
228 const playlistFromGet = res2.body as VideoPlaylist
230 for (const playlist of [ playlistFromGet, playlistFromList ]) {
231 expect(playlist.id).to.be.a('number')
232 expect(playlist.uuid).to.be.a('string')
234 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
236 expect(playlist.displayName).to.equal('my super playlist')
237 expect(playlist.description).to.equal('my super description')
238 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
239 expect(playlist.privacy.label).to.equal('Public')
240 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
241 expect(playlist.type.label).to.equal('Regular')
242 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
244 expect(playlist.videosLength).to.equal(0)
246 expect(playlist.ownerAccount.name).to.equal('root')
247 expect(playlist.ownerAccount.displayName).to.equal('root')
248 expect(playlist.videoChannel.name).to.equal('root_channel')
249 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
254 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
258 const res = await createVideoPlaylist({
260 token: servers[1].accessToken,
262 displayName: 'playlist 2',
263 privacy: VideoPlaylistPrivacy.PUBLIC,
264 videoChannelId: servers[1].videoChannel.id
267 playlistServer2Id1 = res.body.videoPlaylist.id
271 const res = await createVideoPlaylist({
273 token: servers[1].accessToken,
275 displayName: 'playlist 3',
276 privacy: VideoPlaylistPrivacy.PUBLIC,
277 thumbnailfile: 'thumbnail.jpg',
278 videoChannelId: servers[1].videoChannel.id
282 playlistServer2Id2 = res.body.videoPlaylist.id
283 playlistServer2UUID2 = res.body.videoPlaylist.uuid
286 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
287 await addVideoInPlaylist({
289 token: servers[1].accessToken,
291 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
293 await addVideoInPlaylist({
295 token: servers[1].accessToken,
297 elementAttrs: { videoId: servers[1].videos[1].id }
301 await waitJobs(servers)
304 for (const server of [ servers[0], servers[1] ]) {
305 const res = await getVideoPlaylistsList(server.url, 0, 5)
307 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
308 expect(playlist2).to.not.be.undefined
309 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
311 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
312 expect(playlist3).to.not.be.undefined
313 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
316 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
317 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
318 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
321 it('Should have the playlist on server 3 after a new follow', async function () {
324 // Server 2 and server 3 follow each other
325 await doubleFollow(servers[1], servers[2])
327 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
329 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
330 expect(playlist2).to.not.be.undefined
331 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
333 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
337 describe('List playlists', function () {
339 it('Should correctly list the playlists', async function () {
343 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt')
345 expect(res.body.total).to.equal(3)
347 const data: VideoPlaylist[] = res.body.data
348 expect(data).to.have.lengthOf(2)
349 expect(data[0].displayName).to.equal('playlist 2')
350 expect(data[1].displayName).to.equal('playlist 3')
354 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt')
356 expect(res.body.total).to.equal(3)
358 const data: VideoPlaylist[] = res.body.data
359 expect(data).to.have.lengthOf(2)
360 expect(data[0].displayName).to.equal('playlist 2')
361 expect(data[1].displayName).to.equal('my super playlist')
365 it('Should list video channel playlists', async function () {
369 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt')
371 expect(res.body.total).to.equal(1)
373 const data: VideoPlaylist[] = res.body.data
374 expect(data).to.have.lengthOf(1)
375 expect(data[0].displayName).to.equal('my super playlist')
379 it('Should list account playlists', async function () {
383 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt')
385 expect(res.body.total).to.equal(2)
387 const data: VideoPlaylist[] = res.body.data
388 expect(data).to.have.lengthOf(1)
389 expect(data[0].displayName).to.equal('playlist 2')
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 3')
403 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3')
405 expect(res.body.total).to.equal(1)
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', '4')
415 expect(res.body.total).to.equal(0)
417 const data: VideoPlaylist[] = res.body.data
418 expect(data).to.have.lengthOf(0)
423 describe('Playlist rights', function () {
424 let unlistedPlaylist: VideoPlaylistCreateResult
425 let privatePlaylist: VideoPlaylistCreateResult
427 before(async function () {
431 const res = await createVideoPlaylist({
433 token: servers[1].accessToken,
435 displayName: 'playlist unlisted',
436 privacy: VideoPlaylistPrivacy.UNLISTED,
437 videoChannelId: servers[1].videoChannel.id
440 unlistedPlaylist = res.body.videoPlaylist
444 const res = await createVideoPlaylist({
446 token: servers[1].accessToken,
448 displayName: 'playlist private',
449 privacy: VideoPlaylistPrivacy.PRIVATE
452 privatePlaylist = res.body.videoPlaylist
455 await waitJobs(servers)
459 it('Should not list unlisted or private playlists', async function () {
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')
478 it('Should not get unlisted playlist using only the id', async function () {
479 await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404)
482 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
483 await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid)
484 await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID)
487 it('Should not get private playlist without token', async function () {
488 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
489 await getVideoPlaylist(servers[1].url, id, 401)
493 it('Should get private playlist with a token', async function () {
494 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
495 await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id)
500 describe('Update playlists', function () {
502 it('Should update a playlist', async function () {
505 await updateVideoPlaylist({
507 token: servers[1].accessToken,
509 displayName: 'playlist 3 updated',
510 description: 'description updated',
511 privacy: VideoPlaylistPrivacy.UNLISTED,
512 thumbnailfile: 'thumbnail.jpg',
513 videoChannelId: servers[1].videoChannel.id
515 playlistId: playlistServer2Id2
518 await waitJobs(servers)
520 for (const server of servers) {
521 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
522 const playlist: VideoPlaylist = res.body
524 expect(playlist.displayName).to.equal('playlist 3 updated')
525 expect(playlist.description).to.equal('description updated')
527 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
528 expect(playlist.privacy.label).to.equal('Unlisted')
530 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
531 expect(playlist.type.label).to.equal('Regular')
533 expect(playlist.videosLength).to.equal(2)
535 expect(playlist.ownerAccount.name).to.equal('root')
536 expect(playlist.ownerAccount.displayName).to.equal('root')
537 expect(playlist.videoChannel.name).to.equal('root_channel')
538 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
543 describe('Element timestamps', function () {
545 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
548 const addVideo = (elementAttrs: any) => {
549 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
552 const res = await createVideoPlaylist({
554 token: servers[0].accessToken,
556 displayName: 'playlist 4',
557 privacy: VideoPlaylistPrivacy.PUBLIC,
558 videoChannelId: servers[0].videoChannel.id
562 playlistServer1Id = res.body.videoPlaylist.id
563 playlistServer1UUID = res.body.videoPlaylist.uuid
565 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
566 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
567 await addVideo({ videoId: servers[2].videos[2].uuid })
569 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
570 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
574 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
575 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
579 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
580 playlistElementNSFW = res.body.videoPlaylistElement.id
582 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
583 await addVideo({ videoId: nsfwVideoServer1 })
586 await waitJobs(servers)
589 it('Should correctly list playlist videos', async function () {
592 for (const server of servers) {
593 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
595 expect(res.body.total).to.equal(8)
597 const videoElements: VideoPlaylistElement[] = res.body.data
598 expect(videoElements).to.have.lengthOf(8)
600 expect(videoElements[0].video.name).to.equal('video 0 server 1')
601 expect(videoElements[0].position).to.equal(1)
602 expect(videoElements[0].startTimestamp).to.equal(15)
603 expect(videoElements[0].stopTimestamp).to.equal(28)
605 expect(videoElements[1].video.name).to.equal('video 1 server 3')
606 expect(videoElements[1].position).to.equal(2)
607 expect(videoElements[1].startTimestamp).to.equal(35)
608 expect(videoElements[1].stopTimestamp).to.be.null
610 expect(videoElements[2].video.name).to.equal('video 2 server 3')
611 expect(videoElements[2].position).to.equal(3)
612 expect(videoElements[2].startTimestamp).to.be.null
613 expect(videoElements[2].stopTimestamp).to.be.null
615 expect(videoElements[3].video.name).to.equal('video 3 server 1')
616 expect(videoElements[3].position).to.equal(4)
617 expect(videoElements[3].startTimestamp).to.be.null
618 expect(videoElements[3].stopTimestamp).to.equal(35)
620 expect(videoElements[4].video.name).to.equal('video 4 server 1')
621 expect(videoElements[4].position).to.equal(5)
622 expect(videoElements[4].startTimestamp).to.equal(45)
623 expect(videoElements[4].stopTimestamp).to.equal(60)
625 expect(videoElements[5].video.name).to.equal('NSFW video')
626 expect(videoElements[5].position).to.equal(6)
627 expect(videoElements[5].startTimestamp).to.equal(5)
628 expect(videoElements[5].stopTimestamp).to.be.null
630 expect(videoElements[6].video.name).to.equal('NSFW video')
631 expect(videoElements[6].position).to.equal(7)
632 expect(videoElements[6].startTimestamp).to.equal(4)
633 expect(videoElements[6].stopTimestamp).to.be.null
635 expect(videoElements[7].video.name).to.equal('NSFW video')
636 expect(videoElements[7].position).to.equal(8)
637 expect(videoElements[7].startTimestamp).to.be.null
638 expect(videoElements[7].stopTimestamp).to.be.null
640 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
641 expect(res3.body.data).to.have.lengthOf(2)
646 describe('Element type', function () {
647 let groupUser1: ServerInfo[]
648 let groupWithoutToken1: ServerInfo[]
649 let group1: ServerInfo[]
650 let group2: ServerInfo[]
656 before(async function () {
659 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
660 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
661 group1 = [ servers[0] ]
662 group2 = [ servers[1], servers[2] ]
664 const res = await createVideoPlaylist({
666 token: userAccessTokenServer1,
668 displayName: 'playlist 56',
669 privacy: VideoPlaylistPrivacy.PUBLIC,
670 videoChannelId: servers[0].videoChannel.id
674 const playlistServer1Id2 = res.body.videoPlaylist.id
675 playlistServer1UUID2 = res.body.videoPlaylist.uuid
677 const addVideo = (elementAttrs: any) => {
678 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
681 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
682 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
683 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
685 await waitJobs(servers)
687 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
688 await addVideo({ videoId: video2, startTimestamp: 35 })
689 await addVideo({ videoId: video3 })
691 await waitJobs(servers)
694 it('Should update the element type if the video is private', async function () {
697 const name = 'video 89'
701 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
702 await waitJobs(servers)
704 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
705 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
706 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
707 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
711 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
712 await waitJobs(servers)
714 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
715 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
716 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
717 // We deleted the video, so even if we recreated it, the old entry is still deleted
718 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
722 it('Should update the element type if the video is blacklisted', async function () {
725 const name = 'video 89'
729 await servers[0].blacklistCommand.add({ videoId: video1, reason: 'reason', unfederate: true })
730 await waitJobs(servers)
732 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
733 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
734 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
735 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
739 await servers[0].blacklistCommand.remove({ videoId: video1 })
740 await waitJobs(servers)
742 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
743 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
744 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
745 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
750 it('Should update the element type if the account or server of the video is blocked', async function () {
753 const command = servers[0].blocklistCommand
755 const name = 'video 90'
759 await command.addToMyBlocklist({ token: userAccessTokenServer1, account: 'root@localhost:' + servers[1].port })
760 await waitJobs(servers)
762 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
763 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
765 await command.removeFromMyBlocklist({ token: userAccessTokenServer1, account: 'root@localhost:' + servers[1].port })
766 await waitJobs(servers)
768 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
772 await command.addToMyBlocklist({ token: userAccessTokenServer1, server: 'localhost:' + servers[1].port })
773 await waitJobs(servers)
775 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
776 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
778 await command.removeFromMyBlocklist({ token: userAccessTokenServer1, server: 'localhost:' + servers[1].port })
779 await waitJobs(servers)
781 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
785 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
786 await waitJobs(servers)
788 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
789 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
791 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
792 await waitJobs(servers)
794 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
798 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
799 await waitJobs(servers)
801 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
802 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
804 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
805 await waitJobs(servers)
807 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
811 it('Should hide the video if it is NSFW', async function () {
812 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
813 expect(res.body.total).to.equal(3)
815 const elements: VideoPlaylistElement[] = res.body.data
816 const element = elements.find(e => e.position === 3)
818 expect(element).to.exist
819 expect(element.video).to.be.null
820 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
825 describe('Managing playlist elements', function () {
827 it('Should reorder the playlist', async function () {
831 await reorderVideosPlaylist({
833 token: servers[0].accessToken,
834 playlistId: playlistServer1Id,
837 insertAfterPosition: 3
841 await waitJobs(servers)
843 for (const server of servers) {
844 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
845 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
847 expect(names).to.deep.equal([
861 await reorderVideosPlaylist({
863 token: servers[0].accessToken,
864 playlistId: playlistServer1Id,
868 insertAfterPosition: 4
872 await waitJobs(servers)
874 for (const server of servers) {
875 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
876 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
878 expect(names).to.deep.equal([
892 await reorderVideosPlaylist({
894 token: servers[0].accessToken,
895 playlistId: playlistServer1Id,
898 insertAfterPosition: 3
902 await waitJobs(servers)
904 for (const server of servers) {
905 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
906 const elements: VideoPlaylistElement[] = res.body.data
907 const names = elements.map(v => v.video.name)
909 expect(names).to.deep.equal([
920 for (let i = 1; i <= elements.length; i++) {
921 expect(elements[i - 1].position).to.equal(i)
927 it('Should update startTimestamp/endTimestamp of some elements', async function () {
930 await updateVideoPlaylistElement({
932 token: servers[0].accessToken,
933 playlistId: playlistServer1Id,
934 playlistElementId: playlistElementServer1Video4,
940 await updateVideoPlaylistElement({
942 token: servers[0].accessToken,
943 playlistId: playlistServer1Id,
944 playlistElementId: playlistElementServer1Video5,
950 await waitJobs(servers)
952 for (const server of servers) {
953 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
954 const elements: VideoPlaylistElement[] = res.body.data
956 expect(elements[0].video.name).to.equal('video 3 server 1')
957 expect(elements[0].position).to.equal(1)
958 expect(elements[0].startTimestamp).to.equal(1)
959 expect(elements[0].stopTimestamp).to.equal(35)
961 expect(elements[5].video.name).to.equal('video 4 server 1')
962 expect(elements[5].position).to.equal(6)
963 expect(elements[5].startTimestamp).to.equal(45)
964 expect(elements[5].stopTimestamp).to.be.null
968 it('Should check videos existence in my playlist', async function () {
970 servers[0].videos[0].id,
972 servers[0].videos[3].id,
974 servers[0].videos[4].id
976 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
977 const obj = res.body as VideoExistInPlaylist
980 const elem = obj[servers[0].videos[0].id]
981 expect(elem).to.have.lengthOf(1)
982 expect(elem[0].playlistElementId).to.exist
983 expect(elem[0].playlistId).to.equal(playlistServer1Id)
984 expect(elem[0].startTimestamp).to.equal(15)
985 expect(elem[0].stopTimestamp).to.equal(28)
989 const elem = obj[servers[0].videos[3].id]
990 expect(elem).to.have.lengthOf(1)
991 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
992 expect(elem[0].playlistId).to.equal(playlistServer1Id)
993 expect(elem[0].startTimestamp).to.equal(1)
994 expect(elem[0].stopTimestamp).to.equal(35)
998 const elem = obj[servers[0].videos[4].id]
999 expect(elem).to.have.lengthOf(1)
1000 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1001 expect(elem[0].startTimestamp).to.equal(45)
1002 expect(elem[0].stopTimestamp).to.equal(null)
1005 expect(obj[42000]).to.have.lengthOf(0)
1006 expect(obj[43000]).to.have.lengthOf(0)
1009 it('Should automatically update updatedAt field of playlists', async function () {
1010 const server = servers[1]
1011 const videoId = servers[1].videos[5].id
1013 async function getPlaylistNames () {
1014 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
1016 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
1019 const elementAttrs = { videoId }
1020 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
1021 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
1023 const element1 = res1.body.videoPlaylistElement.id
1024 const element2 = res2.body.videoPlaylistElement.id
1026 const names1 = await getPlaylistNames()
1027 expect(names1[0]).to.equal('playlist 3 updated')
1028 expect(names1[1]).to.equal('playlist 2')
1030 await removeVideoFromPlaylist({
1032 token: server.accessToken,
1033 playlistId: playlistServer2Id1,
1034 playlistElementId: element1
1037 const names2 = await getPlaylistNames()
1038 expect(names2[0]).to.equal('playlist 2')
1039 expect(names2[1]).to.equal('playlist 3 updated')
1041 await removeVideoFromPlaylist({
1043 token: server.accessToken,
1044 playlistId: playlistServer2Id2,
1045 playlistElementId: element2
1048 const names3 = await getPlaylistNames()
1049 expect(names3[0]).to.equal('playlist 3 updated')
1050 expect(names3[1]).to.equal('playlist 2')
1053 it('Should delete some elements', async function () {
1056 await removeVideoFromPlaylist({
1057 url: servers[0].url,
1058 token: servers[0].accessToken,
1059 playlistId: playlistServer1Id,
1060 playlistElementId: playlistElementServer1Video4
1063 await removeVideoFromPlaylist({
1064 url: servers[0].url,
1065 token: servers[0].accessToken,
1066 playlistId: playlistServer1Id,
1067 playlistElementId: playlistElementNSFW
1070 await waitJobs(servers)
1072 for (const server of servers) {
1073 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1075 expect(res.body.total).to.equal(6)
1077 const elements: VideoPlaylistElement[] = res.body.data
1078 expect(elements).to.have.lengthOf(6)
1080 expect(elements[0].video.name).to.equal('video 0 server 1')
1081 expect(elements[0].position).to.equal(1)
1083 expect(elements[1].video.name).to.equal('video 2 server 3')
1084 expect(elements[1].position).to.equal(2)
1086 expect(elements[2].video.name).to.equal('video 1 server 3')
1087 expect(elements[2].position).to.equal(3)
1089 expect(elements[3].video.name).to.equal('video 4 server 1')
1090 expect(elements[3].position).to.equal(4)
1092 expect(elements[4].video.name).to.equal('NSFW video')
1093 expect(elements[4].position).to.equal(5)
1095 expect(elements[5].video.name).to.equal('NSFW video')
1096 expect(elements[5].position).to.equal(6)
1100 it('Should be able to create a public playlist, and set it to private', async function () {
1103 const res = await createVideoPlaylist({
1104 url: servers[0].url,
1105 token: servers[0].accessToken,
1107 displayName: 'my super public playlist',
1108 privacy: VideoPlaylistPrivacy.PUBLIC,
1109 videoChannelId: servers[0].videoChannel.id
1112 const videoPlaylistIds = res.body.videoPlaylist
1114 await waitJobs(servers)
1116 for (const server of servers) {
1117 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1120 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1121 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1123 await waitJobs(servers)
1125 for (const server of [ servers[1], servers[2] ]) {
1126 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404)
1128 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1130 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1134 describe('Playlist deletion', function () {
1136 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1139 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1141 await waitJobs(servers)
1143 for (const server of servers) {
1144 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404)
1148 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1151 for (const server of servers) {
1152 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1156 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1159 const finder = data => data.find(p => p.displayName === 'my super playlist')
1162 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1163 expect(res.body.total).to.equal(3)
1164 expect(finder(res.body.data)).to.not.be.undefined
1167 await servers[2].followsCommand.unfollow({ target: servers[0] })
1170 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1171 expect(res.body.total).to.equal(1)
1173 expect(finder(res.body.data)).to.be.undefined
1177 it('Should delete a channel and put the associated playlist in private mode', async function () {
1180 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1181 const videoChannelId = res.body.videoChannel.id
1183 const res2 = await createVideoPlaylist({
1184 url: servers[0].url,
1185 token: servers[0].accessToken,
1187 displayName: 'channel playlist',
1188 privacy: VideoPlaylistPrivacy.PUBLIC,
1192 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1194 await waitJobs(servers)
1196 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1198 await waitJobs(servers)
1200 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1201 expect(res3.body.displayName).to.equal('channel playlist')
1202 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1204 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404)
1207 it('Should delete an account and delete its playlists', async function () {
1210 const user = { username: 'user_1', password: 'password' }
1211 const res = await createUser({
1212 url: servers[0].url,
1213 accessToken: servers[0].accessToken,
1214 username: user.username,
1215 password: user.password
1218 const userId = res.body.user.id
1219 const userAccessToken = await userLogin(servers[0], user)
1221 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1222 const userChannel = (resChannel.body as User).videoChannels[0]
1224 await createVideoPlaylist({
1225 url: servers[0].url,
1226 token: userAccessToken,
1228 displayName: 'playlist to be deleted',
1229 privacy: VideoPlaylistPrivacy.PUBLIC,
1230 videoChannelId: userChannel.id
1234 await waitJobs(servers)
1236 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1239 for (const server of [ servers[0], servers[1] ]) {
1240 const res = await getVideoPlaylistsList(server.url, 0, 15)
1241 expect(finder(res.body.data)).to.not.be.undefined
1245 await removeUser(servers[0].url, userId, servers[0].accessToken)
1246 await waitJobs(servers)
1249 for (const server of [ servers[0], servers[1] ]) {
1250 const res = await getVideoPlaylistsList(server.url, 0, 15)
1251 expect(finder(res.body.data)).to.be.undefined
1257 after(async function () {
1258 await cleanupTests(servers)