1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import * as chai from 'chai'
9 checkPlaylistFilesWereRemoved,
16 doVideosExistInMyPlaylist,
17 flushAndRunMultipleServers,
18 generateUserAccessToken,
20 getAccountPlaylistsList,
21 getAccountPlaylistsListWithToken,
24 getVideoChannelPlaylistsList,
26 getVideoPlaylistPrivacies,
27 getVideoPlaylistsList,
28 getVideoPlaylistWithToken,
30 removeVideoFromBlacklist,
31 removeVideoFromPlaylist,
32 reorderVideosPlaylist,
34 setAccessTokensToServers,
35 setDefaultVideoChannel,
40 updateVideoPlaylistElement,
45 } from '../../../../shared/extra-utils'
46 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
47 import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
48 import { VideoPrivacy } from '../../../../shared/models/videos'
49 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
50 import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
51 import { User } from '../../../../shared/models/users'
52 import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
54 addAccountToAccountBlocklist,
55 addAccountToServerBlocklist,
56 addServerToAccountBlocklist,
57 addServerToServerBlocklist,
58 removeAccountFromAccountBlocklist,
59 removeAccountFromServerBlocklist,
60 removeServerFromAccountBlocklist,
61 removeServerFromServerBlocklist
62 } from '../../../../shared/extra-utils/users/blocklist'
64 const expect = chai.expect
66 async function checkPlaylistElementType (
67 servers: ServerInfo[],
69 type: VideoPlaylistElementType,
74 for (const server of servers) {
75 const res = await getPlaylistVideos(server.url, server.accessToken, playlistId, 0, 10)
76 expect(res.body.total).to.equal(total)
78 const videoElement: VideoPlaylistElement = res.body.data.find((e: VideoPlaylistElement) => e.position === position)
79 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
81 if (type === VideoPlaylistElementType.REGULAR) {
82 expect(videoElement.video).to.not.be.null
83 expect(videoElement.video.name).to.equal(name)
85 expect(videoElement.video).to.be.null
90 describe('Test video playlists', function () {
91 let servers: ServerInfo[] = []
93 let playlistServer2Id1: number
94 let playlistServer2Id2: number
95 let playlistServer2UUID2: number
97 let playlistServer1Id: number
98 let playlistServer1UUID: string
99 let playlistServer1UUID2: string
101 let playlistElementServer1Video4: number
102 let playlistElementServer1Video5: number
103 let playlistElementNSFW: number
105 let nsfwVideoServer1: number
107 let userAccessTokenServer1: string
109 before(async function () {
112 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
114 // Get the access tokens
115 await setAccessTokensToServers(servers)
116 await setDefaultVideoChannel(servers)
118 // Server 1 and server 2 follow each other
119 await doubleFollow(servers[0], servers[1])
120 // Server 1 and server 3 follow each other
121 await doubleFollow(servers[0], servers[2])
124 const serverPromises: Promise<any>[][] = []
126 for (const server of servers) {
127 const videoPromises: Promise<any>[] = []
129 for (let i = 0; i < 7; i++) {
131 uploadVideo(server.url, server.accessToken, { name: `video ${i} server ${server.serverNumber}`, nsfw: false })
132 .then(res => res.body.video)
136 serverPromises.push(videoPromises)
139 servers[0].videos = await Promise.all(serverPromises[0])
140 servers[1].videos = await Promise.all(serverPromises[1])
141 servers[2].videos = await Promise.all(serverPromises[2])
144 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
149 accessToken: servers[0].accessToken,
153 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
156 await waitJobs(servers)
159 describe('Get default playlists', function () {
160 it('Should list video playlist privacies', async function () {
161 const res = await getVideoPlaylistPrivacies(servers[0].url)
163 const privacies = res.body
164 expect(Object.keys(privacies)).to.have.length.at.least(3)
166 expect(privacies[3]).to.equal('Private')
169 it('Should list watch later playlist', async function () {
170 const url = servers[0].url
171 const accessToken = servers[0].accessToken
174 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
176 expect(res.body.total).to.equal(1)
177 expect(res.body.data).to.have.lengthOf(1)
179 const playlist: VideoPlaylist = res.body.data[0]
180 expect(playlist.displayName).to.equal('Watch later')
181 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
182 expect(playlist.type.label).to.equal('Watch later')
186 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
188 expect(res.body.total).to.equal(0)
189 expect(res.body.data).to.have.lengthOf(0)
193 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
194 expect(res.body.total).to.equal(0)
195 expect(res.body.data).to.have.lengthOf(0)
199 it('Should get private playlist for a classic user', async function () {
200 const token = await generateUserAccessToken(servers[0], 'toto')
202 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
204 expect(res.body.total).to.equal(1)
205 expect(res.body.data).to.have.lengthOf(1)
207 const playlistId = res.body.data[0].id
208 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
212 describe('Create and federate playlists', function () {
214 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
217 await createVideoPlaylist({
219 token: servers[0].accessToken,
221 displayName: 'my super playlist',
222 privacy: VideoPlaylistPrivacy.PUBLIC,
223 description: 'my super description',
224 thumbnailfile: 'thumbnail.jpg',
225 videoChannelId: servers[0].videoChannel.id
229 await waitJobs(servers)
231 for (const server of servers) {
232 const res = await getVideoPlaylistsList(server.url, 0, 5)
233 expect(res.body.total).to.equal(1)
234 expect(res.body.data).to.have.lengthOf(1)
236 const playlistFromList = res.body.data[0] as VideoPlaylist
238 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
239 const playlistFromGet = res2.body as VideoPlaylist
241 for (const playlist of [ playlistFromGet, playlistFromList ]) {
242 expect(playlist.id).to.be.a('number')
243 expect(playlist.uuid).to.be.a('string')
245 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
247 expect(playlist.displayName).to.equal('my super playlist')
248 expect(playlist.description).to.equal('my super description')
249 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
250 expect(playlist.privacy.label).to.equal('Public')
251 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
252 expect(playlist.type.label).to.equal('Regular')
253 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
255 expect(playlist.videosLength).to.equal(0)
257 expect(playlist.ownerAccount.name).to.equal('root')
258 expect(playlist.ownerAccount.displayName).to.equal('root')
259 expect(playlist.videoChannel.name).to.equal('root_channel')
260 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
265 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
269 const res = await createVideoPlaylist({
271 token: servers[1].accessToken,
273 displayName: 'playlist 2',
274 privacy: VideoPlaylistPrivacy.PUBLIC,
275 videoChannelId: servers[1].videoChannel.id
278 playlistServer2Id1 = res.body.videoPlaylist.id
282 const res = await createVideoPlaylist({
284 token: servers[1].accessToken,
286 displayName: 'playlist 3',
287 privacy: VideoPlaylistPrivacy.PUBLIC,
288 thumbnailfile: 'thumbnail.jpg',
289 videoChannelId: servers[1].videoChannel.id
293 playlistServer2Id2 = res.body.videoPlaylist.id
294 playlistServer2UUID2 = res.body.videoPlaylist.uuid
297 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
298 await addVideoInPlaylist({
300 token: servers[1].accessToken,
302 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
304 await addVideoInPlaylist({
306 token: servers[1].accessToken,
308 elementAttrs: { videoId: servers[1].videos[1].id }
312 await waitJobs(servers)
314 for (const server of [ servers[0], servers[1] ]) {
315 const res = await getVideoPlaylistsList(server.url, 0, 5)
317 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
318 expect(playlist2).to.not.be.undefined
319 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
321 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
322 expect(playlist3).to.not.be.undefined
323 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
326 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
327 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
328 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
331 it('Should have the playlist on server 3 after a new follow', async function () {
334 // Server 2 and server 3 follow each other
335 await doubleFollow(servers[1], servers[2])
337 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
339 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
340 expect(playlist2).to.not.be.undefined
341 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
343 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
347 describe('List playlists', function () {
349 it('Should correctly list the playlists', async function () {
353 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt')
355 expect(res.body.total).to.equal(3)
357 const data: VideoPlaylist[] = res.body.data
358 expect(data).to.have.lengthOf(2)
359 expect(data[0].displayName).to.equal('playlist 2')
360 expect(data[1].displayName).to.equal('playlist 3')
364 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt')
366 expect(res.body.total).to.equal(3)
368 const data: VideoPlaylist[] = res.body.data
369 expect(data).to.have.lengthOf(2)
370 expect(data[0].displayName).to.equal('playlist 2')
371 expect(data[1].displayName).to.equal('my super playlist')
375 it('Should list video channel playlists', async function () {
379 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt')
381 expect(res.body.total).to.equal(1)
383 const data: VideoPlaylist[] = res.body.data
384 expect(data).to.have.lengthOf(1)
385 expect(data[0].displayName).to.equal('my super playlist')
389 it('Should list account playlists', async function () {
393 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt')
395 expect(res.body.total).to.equal(2)
397 const data: VideoPlaylist[] = res.body.data
398 expect(data).to.have.lengthOf(1)
399 expect(data[0].displayName).to.equal('playlist 2')
403 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt')
405 expect(res.body.total).to.equal(2)
407 const data: VideoPlaylist[] = res.body.data
408 expect(data).to.have.lengthOf(1)
409 expect(data[0].displayName).to.equal('playlist 3')
413 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3')
415 expect(res.body.total).to.equal(1)
417 const data: VideoPlaylist[] = res.body.data
418 expect(data).to.have.lengthOf(1)
419 expect(data[0].displayName).to.equal('playlist 3')
423 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4')
425 expect(res.body.total).to.equal(0)
427 const data: VideoPlaylist[] = res.body.data
428 expect(data).to.have.lengthOf(0)
432 it('Should not list unlisted or private playlists', async function () {
435 await createVideoPlaylist({
437 token: servers[1].accessToken,
439 displayName: 'playlist unlisted',
440 privacy: VideoPlaylistPrivacy.UNLISTED
444 await createVideoPlaylist({
446 token: servers[1].accessToken,
448 displayName: 'playlist private',
449 privacy: VideoPlaylistPrivacy.PRIVATE
453 await waitJobs(servers)
455 for (const server of servers) {
457 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
458 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
461 expect(results[0].body.total).to.equal(2)
462 expect(results[1].body.total).to.equal(3)
464 for (const res of results) {
465 const data: VideoPlaylist[] = res.body.data
466 expect(data).to.have.lengthOf(2)
467 expect(data[0].displayName).to.equal('playlist 3')
468 expect(data[1].displayName).to.equal('playlist 2')
474 describe('Update playlists', function () {
476 it('Should update a playlist', async function () {
479 await updateVideoPlaylist({
481 token: servers[1].accessToken,
483 displayName: 'playlist 3 updated',
484 description: 'description updated',
485 privacy: VideoPlaylistPrivacy.UNLISTED,
486 thumbnailfile: 'thumbnail.jpg',
487 videoChannelId: servers[1].videoChannel.id
489 playlistId: playlistServer2Id2
492 await waitJobs(servers)
494 for (const server of servers) {
495 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
496 const playlist: VideoPlaylist = res.body
498 expect(playlist.displayName).to.equal('playlist 3 updated')
499 expect(playlist.description).to.equal('description updated')
501 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
502 expect(playlist.privacy.label).to.equal('Unlisted')
504 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
505 expect(playlist.type.label).to.equal('Regular')
507 expect(playlist.videosLength).to.equal(2)
509 expect(playlist.ownerAccount.name).to.equal('root')
510 expect(playlist.ownerAccount.displayName).to.equal('root')
511 expect(playlist.videoChannel.name).to.equal('root_channel')
512 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
517 describe('Element timestamps', function () {
519 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
522 const addVideo = (elementAttrs: any) => {
523 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
526 const res = await createVideoPlaylist({
528 token: servers[0].accessToken,
530 displayName: 'playlist 4',
531 privacy: VideoPlaylistPrivacy.PUBLIC,
532 videoChannelId: servers[0].videoChannel.id
536 playlistServer1Id = res.body.videoPlaylist.id
537 playlistServer1UUID = res.body.videoPlaylist.uuid
539 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
540 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
541 await addVideo({ videoId: servers[2].videos[2].uuid })
543 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
544 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
548 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
549 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
553 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
554 playlistElementNSFW = res.body.videoPlaylistElement.id
557 await waitJobs(servers)
560 it('Should correctly list playlist videos', async function () {
563 for (const server of servers) {
564 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
566 expect(res.body.total).to.equal(6)
568 const videoElements: VideoPlaylistElement[] = res.body.data
569 expect(videoElements).to.have.lengthOf(6)
571 expect(videoElements[0].video.name).to.equal('video 0 server 1')
572 expect(videoElements[0].position).to.equal(1)
573 expect(videoElements[0].startTimestamp).to.equal(15)
574 expect(videoElements[0].stopTimestamp).to.equal(28)
576 expect(videoElements[1].video.name).to.equal('video 1 server 3')
577 expect(videoElements[1].position).to.equal(2)
578 expect(videoElements[1].startTimestamp).to.equal(35)
579 expect(videoElements[1].stopTimestamp).to.be.null
581 expect(videoElements[2].video.name).to.equal('video 2 server 3')
582 expect(videoElements[2].position).to.equal(3)
583 expect(videoElements[2].startTimestamp).to.be.null
584 expect(videoElements[2].stopTimestamp).to.be.null
586 expect(videoElements[3].video.name).to.equal('video 3 server 1')
587 expect(videoElements[3].position).to.equal(4)
588 expect(videoElements[3].startTimestamp).to.be.null
589 expect(videoElements[3].stopTimestamp).to.equal(35)
591 expect(videoElements[4].video.name).to.equal('video 4 server 1')
592 expect(videoElements[4].position).to.equal(5)
593 expect(videoElements[4].startTimestamp).to.equal(45)
594 expect(videoElements[4].stopTimestamp).to.equal(60)
596 expect(videoElements[5].video.name).to.equal('NSFW video')
597 expect(videoElements[5].position).to.equal(6)
598 expect(videoElements[5].startTimestamp).to.equal(5)
599 expect(videoElements[5].stopTimestamp).to.be.null
601 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
602 expect(res3.body.data).to.have.lengthOf(2)
607 describe('Element type', function () {
608 let groupUser1: ServerInfo[]
609 let groupWithoutToken1: ServerInfo[]
610 let group1: ServerInfo[]
611 let group2: ServerInfo[]
617 before(async function () {
620 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
621 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
622 group1 = [ servers[0] ]
623 group2 = [ servers[1], servers[2] ]
625 const res = await createVideoPlaylist({
627 token: userAccessTokenServer1,
629 displayName: 'playlist 56',
630 privacy: VideoPlaylistPrivacy.PUBLIC,
631 videoChannelId: servers[0].videoChannel.id
635 const playlistServer1Id2 = res.body.videoPlaylist.id
636 playlistServer1UUID2 = res.body.videoPlaylist.uuid
638 const addVideo = (elementAttrs: any) => {
639 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
642 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
643 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
644 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
646 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
647 await addVideo({ videoId: video2, startTimestamp: 35 })
648 await addVideo({ videoId: video3 })
650 await waitJobs(servers)
653 it('Should update the element type if the video is private', async function () {
656 const name = 'video 89'
660 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
661 await waitJobs(servers)
663 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
664 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
665 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
666 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
670 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
671 await waitJobs(servers)
673 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
674 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
675 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
676 // We deleted the video, so even if we recreated it, the old entry is still deleted
677 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
681 it('Should update the element type if the video is blacklisted', async function () {
684 const name = 'video 89'
688 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
689 await waitJobs(servers)
691 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
692 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
693 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
694 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
698 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
699 await waitJobs(servers)
701 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
702 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
703 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
704 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
705 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
709 it('Should update the element type if the account or server of the video is blocked', async function () {
712 const name = 'video 90'
716 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
717 await waitJobs(servers)
719 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
720 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
722 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
723 await waitJobs(servers)
725 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
729 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
730 await waitJobs(servers)
732 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
733 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
735 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
736 await waitJobs(servers)
738 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
742 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
743 await waitJobs(servers)
745 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
748 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
749 await waitJobs(servers)
751 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
755 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
756 await waitJobs(servers)
758 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
759 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
761 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
762 await waitJobs(servers)
764 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
768 it('Should hide the video if it is NSFW', async function () {
769 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
770 expect(res.body.total).to.equal(3)
772 const elements: VideoPlaylistElement[] = res.body.data
773 const element = elements.find(e => e.position === 3)
775 expect(element).to.exist
776 expect(element.video).to.be.null
777 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
782 describe('Managing playlist elements', function () {
784 it('Should reorder the playlist', async function () {
788 await reorderVideosPlaylist({
790 token: servers[0].accessToken,
791 playlistId: playlistServer1Id,
794 insertAfterPosition: 3
798 await waitJobs(servers)
800 for (const server of servers) {
801 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
802 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
804 expect(names).to.deep.equal([
816 await reorderVideosPlaylist({
818 token: servers[0].accessToken,
819 playlistId: playlistServer1Id,
823 insertAfterPosition: 4
827 await waitJobs(servers)
829 for (const server of servers) {
830 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
831 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
833 expect(names).to.deep.equal([
845 await reorderVideosPlaylist({
847 token: servers[0].accessToken,
848 playlistId: playlistServer1Id,
851 insertAfterPosition: 3
855 await waitJobs(servers)
857 for (const server of servers) {
858 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
859 const elements: VideoPlaylistElement[] = res.body.data
860 const names = elements.map(v => v.video.name)
862 expect(names).to.deep.equal([
871 for (let i = 1; i <= elements.length; i++) {
872 expect(elements[i - 1].position).to.equal(i)
878 it('Should update startTimestamp/endTimestamp of some elements', async function () {
881 await updateVideoPlaylistElement({
883 token: servers[0].accessToken,
884 playlistId: playlistServer1Id,
885 playlistElementId: playlistElementServer1Video4,
891 await updateVideoPlaylistElement({
893 token: servers[0].accessToken,
894 playlistId: playlistServer1Id,
895 playlistElementId: playlistElementServer1Video5,
901 await waitJobs(servers)
903 for (const server of servers) {
904 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
905 const elements: VideoPlaylistElement[] = res.body.data
907 expect(elements[0].video.name).to.equal('video 3 server 1')
908 expect(elements[0].position).to.equal(1)
909 expect(elements[0].startTimestamp).to.equal(1)
910 expect(elements[0].stopTimestamp).to.equal(35)
912 expect(elements[5].video.name).to.equal('video 4 server 1')
913 expect(elements[5].position).to.equal(6)
914 expect(elements[5].startTimestamp).to.equal(45)
915 expect(elements[5].stopTimestamp).to.be.null
919 it('Should check videos existence in my playlist', async function () {
921 servers[0].videos[0].id,
923 servers[0].videos[3].id,
925 servers[0].videos[4].id
927 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
928 const obj = res.body as VideoExistInPlaylist
931 const elem = obj[servers[0].videos[0].id]
932 expect(elem).to.have.lengthOf(1)
933 expect(elem[0].playlistElementId).to.exist
934 expect(elem[0].playlistId).to.equal(playlistServer1Id)
935 expect(elem[0].startTimestamp).to.equal(15)
936 expect(elem[0].stopTimestamp).to.equal(28)
940 const elem = obj[servers[0].videos[3].id]
941 expect(elem).to.have.lengthOf(1)
942 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
943 expect(elem[0].playlistId).to.equal(playlistServer1Id)
944 expect(elem[0].startTimestamp).to.equal(1)
945 expect(elem[0].stopTimestamp).to.equal(35)
949 const elem = obj[servers[0].videos[4].id]
950 expect(elem).to.have.lengthOf(1)
951 expect(elem[0].playlistId).to.equal(playlistServer1Id)
952 expect(elem[0].startTimestamp).to.equal(45)
953 expect(elem[0].stopTimestamp).to.equal(null)
956 expect(obj[42000]).to.have.lengthOf(0)
957 expect(obj[43000]).to.have.lengthOf(0)
960 it('Should automatically update updatedAt field of playlists', async function () {
961 const server = servers[1]
962 const videoId = servers[1].videos[5].id
964 async function getPlaylistNames () {
965 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
967 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
970 const elementAttrs = { videoId }
971 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
972 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
974 const element1 = res1.body.videoPlaylistElement.id
975 const element2 = res2.body.videoPlaylistElement.id
977 const names1 = await getPlaylistNames()
978 expect(names1[0]).to.equal('playlist 3 updated')
979 expect(names1[1]).to.equal('playlist 2')
981 await removeVideoFromPlaylist({
983 token: server.accessToken,
984 playlistId: playlistServer2Id1,
985 playlistElementId: element1
988 const names2 = await getPlaylistNames()
989 expect(names2[0]).to.equal('playlist 2')
990 expect(names2[1]).to.equal('playlist 3 updated')
992 await removeVideoFromPlaylist({
994 token: server.accessToken,
995 playlistId: playlistServer2Id2,
996 playlistElementId: element2
999 const names3 = await getPlaylistNames()
1000 expect(names3[0]).to.equal('playlist 3 updated')
1001 expect(names3[1]).to.equal('playlist 2')
1004 it('Should delete some elements', async function () {
1007 await removeVideoFromPlaylist({
1008 url: servers[0].url,
1009 token: servers[0].accessToken,
1010 playlistId: playlistServer1Id,
1011 playlistElementId: playlistElementServer1Video4
1014 await removeVideoFromPlaylist({
1015 url: servers[0].url,
1016 token: servers[0].accessToken,
1017 playlistId: playlistServer1Id,
1018 playlistElementId: playlistElementNSFW
1021 await waitJobs(servers)
1023 for (const server of servers) {
1024 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1026 expect(res.body.total).to.equal(4)
1028 const elements: VideoPlaylistElement[] = res.body.data
1029 expect(elements).to.have.lengthOf(4)
1031 expect(elements[0].video.name).to.equal('video 0 server 1')
1032 expect(elements[0].position).to.equal(1)
1034 expect(elements[1].video.name).to.equal('video 2 server 3')
1035 expect(elements[1].position).to.equal(2)
1037 expect(elements[2].video.name).to.equal('video 1 server 3')
1038 expect(elements[2].position).to.equal(3)
1040 expect(elements[3].video.name).to.equal('video 4 server 1')
1041 expect(elements[3].position).to.equal(4)
1045 it('Should be able to create a public playlist, and set it to private', async function () {
1048 const res = await createVideoPlaylist({
1049 url: servers[0].url,
1050 token: servers[0].accessToken,
1052 displayName: 'my super public playlist',
1053 privacy: VideoPlaylistPrivacy.PUBLIC,
1054 videoChannelId: servers[0].videoChannel.id
1057 const videoPlaylistIds = res.body.videoPlaylist
1059 await waitJobs(servers)
1061 for (const server of servers) {
1062 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 200)
1065 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1066 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1068 await waitJobs(servers)
1070 for (const server of [ servers[1], servers[2] ]) {
1071 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 404)
1073 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, 401)
1075 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, 200)
1079 describe('Playlist deletion', function () {
1081 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1084 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1086 await waitJobs(servers)
1088 for (const server of servers) {
1089 await getVideoPlaylist(server.url, playlistServer1UUID, 404)
1093 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1096 for (const server of servers) {
1097 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1101 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1104 const finder = data => data.find(p => p.displayName === 'my super playlist')
1107 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1108 expect(res.body.total).to.equal(3)
1109 expect(finder(res.body.data)).to.not.be.undefined
1112 await unfollow(servers[2].url, servers[2].accessToken, servers[0])
1115 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1116 expect(res.body.total).to.equal(1)
1118 expect(finder(res.body.data)).to.be.undefined
1122 it('Should delete a channel and put the associated playlist in private mode', async function () {
1125 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1126 const videoChannelId = res.body.videoChannel.id
1128 const res2 = await createVideoPlaylist({
1129 url: servers[0].url,
1130 token: servers[0].accessToken,
1132 displayName: 'channel playlist',
1133 privacy: VideoPlaylistPrivacy.PUBLIC,
1137 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1139 await waitJobs(servers)
1141 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1143 await waitJobs(servers)
1145 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1146 expect(res3.body.displayName).to.equal('channel playlist')
1147 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1149 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, 404)
1152 it('Should delete an account and delete its playlists', async function () {
1155 const user = { username: 'user_1', password: 'password' }
1156 const res = await createUser({
1157 url: servers[0].url,
1158 accessToken: servers[0].accessToken,
1159 username: user.username,
1160 password: user.password
1163 const userId = res.body.user.id
1164 const userAccessToken = await userLogin(servers[0], user)
1166 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1167 const userChannel = (resChannel.body as User).videoChannels[0]
1169 await createVideoPlaylist({
1170 url: servers[0].url,
1171 token: userAccessToken,
1173 displayName: 'playlist to be deleted',
1174 privacy: VideoPlaylistPrivacy.PUBLIC,
1175 videoChannelId: userChannel.id
1179 await waitJobs(servers)
1181 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1184 for (const server of [ servers[0], servers[1] ]) {
1185 const res = await getVideoPlaylistsList(server.url, 0, 15)
1186 expect(finder(res.body.data)).to.not.be.undefined
1190 await removeUser(servers[0].url, userId, servers[0].accessToken)
1191 await waitJobs(servers)
1194 for (const server of [ servers[0], servers[1] ]) {
1195 const res = await getVideoPlaylistsList(server.url, 0, 15)
1196 expect(finder(res.body.data)).to.be.undefined
1202 after(async function () {
1203 await cleanupTests(servers)