1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import * as chai from 'chai'
9 checkPlaylistFilesWereRemoved,
16 doVideosExistInMyPlaylist,
17 flushAndRunMultipleServers,
18 generateUserAccessToken,
20 getAccountPlaylistsList,
21 getAccountPlaylistsListWithToken,
24 getVideoChannelPlaylistsList,
26 getVideoPlaylistPrivacies,
27 getVideoPlaylistsList,
28 getVideoPlaylistWithToken,
30 removeVideoFromBlacklist,
31 removeVideoFromPlaylist,
32 reorderVideosPlaylist,
34 setAccessTokensToServers,
35 setDefaultVideoChannel,
40 updateVideoPlaylistElement,
46 } from '../../../../shared/extra-utils'
47 import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
48 import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
49 import { VideoPrivacy } from '../../../../shared/models/videos'
50 import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
51 import { VideoExistInPlaylist } from '../../../../shared/models/videos/playlist/video-exist-in-playlist.model'
52 import { User } from '../../../../shared/models/users'
53 import { VideoPlaylistElement, VideoPlaylistElementType } from '../../../../shared/models/videos/playlist/video-playlist-element.model'
55 addAccountToAccountBlocklist,
56 addAccountToServerBlocklist,
57 addServerToAccountBlocklist,
58 addServerToServerBlocklist,
59 removeAccountFromAccountBlocklist,
60 removeAccountFromServerBlocklist,
61 removeServerFromAccountBlocklist,
62 removeServerFromServerBlocklist
63 } from '../../../../shared/extra-utils/users/blocklist'
64 import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
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 const serverPromises: Promise<any>[][] = []
128 for (const server of servers) {
129 const videoPromises: Promise<any>[] = []
131 for (let i = 0; i < 7; i++) {
133 uploadVideo(server.url, server.accessToken, { name: `video ${i} server ${server.serverNumber}`, nsfw: false })
134 .then(res => res.body.video)
138 serverPromises.push(videoPromises)
141 servers[0].videos = await Promise.all(serverPromises[0])
142 servers[1].videos = await Promise.all(serverPromises[1])
143 servers[2].videos = await Promise.all(serverPromises[2])
146 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
151 accessToken: servers[0].accessToken,
155 userAccessTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
158 await waitJobs(servers)
161 describe('Get default playlists', function () {
162 it('Should list video playlist privacies', async function () {
163 const res = await getVideoPlaylistPrivacies(servers[0].url)
165 const privacies = res.body
166 expect(Object.keys(privacies)).to.have.length.at.least(3)
168 expect(privacies[3]).to.equal('Private')
171 it('Should list watch later playlist', async function () {
172 const url = servers[0].url
173 const accessToken = servers[0].accessToken
176 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER)
178 expect(res.body.total).to.equal(1)
179 expect(res.body.data).to.have.lengthOf(1)
181 const playlist: VideoPlaylist = res.body.data[0]
182 expect(playlist.displayName).to.equal('Watch later')
183 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
184 expect(playlist.type.label).to.equal('Watch later')
188 const res = await getAccountPlaylistsListWithToken(url, accessToken, 'root', 0, 5, VideoPlaylistType.REGULAR)
190 expect(res.body.total).to.equal(0)
191 expect(res.body.data).to.have.lengthOf(0)
195 const res = await getAccountPlaylistsList(url, 'root', 0, 5)
196 expect(res.body.total).to.equal(0)
197 expect(res.body.data).to.have.lengthOf(0)
201 it('Should get private playlist for a classic user', async function () {
202 const token = await generateUserAccessToken(servers[0], 'toto')
204 const res = await getAccountPlaylistsListWithToken(servers[0].url, token, 'toto', 0, 5)
206 expect(res.body.total).to.equal(1)
207 expect(res.body.data).to.have.lengthOf(1)
209 const playlistId = res.body.data[0].id
210 await getPlaylistVideos(servers[0].url, token, playlistId, 0, 5)
214 describe('Create and federate playlists', function () {
216 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
219 await createVideoPlaylist({
221 token: servers[0].accessToken,
223 displayName: 'my super playlist',
224 privacy: VideoPlaylistPrivacy.PUBLIC,
225 description: 'my super description',
226 thumbnailfile: 'thumbnail.jpg',
227 videoChannelId: servers[0].videoChannel.id
231 await waitJobs(servers)
232 // Processing a playlist by the receiver could be long
235 for (const server of servers) {
236 const res = await getVideoPlaylistsList(server.url, 0, 5)
237 expect(res.body.total).to.equal(1)
238 expect(res.body.data).to.have.lengthOf(1)
240 const playlistFromList = res.body.data[0] as VideoPlaylist
242 const res2 = await getVideoPlaylist(server.url, playlistFromList.uuid)
243 const playlistFromGet = res2.body as VideoPlaylist
245 for (const playlist of [ playlistFromGet, playlistFromList ]) {
246 expect(playlist.id).to.be.a('number')
247 expect(playlist.uuid).to.be.a('string')
249 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
251 expect(playlist.displayName).to.equal('my super playlist')
252 expect(playlist.description).to.equal('my super description')
253 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
254 expect(playlist.privacy.label).to.equal('Public')
255 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
256 expect(playlist.type.label).to.equal('Regular')
257 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
259 expect(playlist.videosLength).to.equal(0)
261 expect(playlist.ownerAccount.name).to.equal('root')
262 expect(playlist.ownerAccount.displayName).to.equal('root')
263 expect(playlist.videoChannel.name).to.equal('root_channel')
264 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
269 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
273 const res = await createVideoPlaylist({
275 token: servers[1].accessToken,
277 displayName: 'playlist 2',
278 privacy: VideoPlaylistPrivacy.PUBLIC,
279 videoChannelId: servers[1].videoChannel.id
282 playlistServer2Id1 = res.body.videoPlaylist.id
286 const res = await createVideoPlaylist({
288 token: servers[1].accessToken,
290 displayName: 'playlist 3',
291 privacy: VideoPlaylistPrivacy.PUBLIC,
292 thumbnailfile: 'thumbnail.jpg',
293 videoChannelId: servers[1].videoChannel.id
297 playlistServer2Id2 = res.body.videoPlaylist.id
298 playlistServer2UUID2 = res.body.videoPlaylist.uuid
301 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
302 await addVideoInPlaylist({
304 token: servers[1].accessToken,
306 elementAttrs: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
308 await addVideoInPlaylist({
310 token: servers[1].accessToken,
312 elementAttrs: { videoId: servers[1].videos[1].id }
316 await waitJobs(servers)
319 for (const server of [ servers[0], servers[1] ]) {
320 const res = await getVideoPlaylistsList(server.url, 0, 5)
322 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
323 expect(playlist2).to.not.be.undefined
324 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
326 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
327 expect(playlist3).to.not.be.undefined
328 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
331 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
332 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
333 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
336 it('Should have the playlist on server 3 after a new follow', async function () {
339 // Server 2 and server 3 follow each other
340 await doubleFollow(servers[1], servers[2])
342 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
344 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
345 expect(playlist2).to.not.be.undefined
346 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
348 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
352 describe('List playlists', function () {
354 it('Should correctly list the playlists', async function () {
358 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, 'createdAt')
360 expect(res.body.total).to.equal(3)
362 const data: VideoPlaylist[] = res.body.data
363 expect(data).to.have.lengthOf(2)
364 expect(data[0].displayName).to.equal('playlist 2')
365 expect(data[1].displayName).to.equal('playlist 3')
369 const res = await getVideoPlaylistsList(servers[2].url, 1, 2, '-createdAt')
371 expect(res.body.total).to.equal(3)
373 const data: VideoPlaylist[] = res.body.data
374 expect(data).to.have.lengthOf(2)
375 expect(data[0].displayName).to.equal('playlist 2')
376 expect(data[1].displayName).to.equal('my super playlist')
380 it('Should list video channel playlists', async function () {
384 const res = await getVideoChannelPlaylistsList(servers[0].url, 'root_channel', 0, 2, '-createdAt')
386 expect(res.body.total).to.equal(1)
388 const data: VideoPlaylist[] = res.body.data
389 expect(data).to.have.lengthOf(1)
390 expect(data[0].displayName).to.equal('my super playlist')
394 it('Should list account playlists', async function () {
398 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, '-createdAt')
400 expect(res.body.total).to.equal(2)
402 const data: VideoPlaylist[] = res.body.data
403 expect(data).to.have.lengthOf(1)
404 expect(data[0].displayName).to.equal('playlist 2')
408 const res = await getAccountPlaylistsList(servers[1].url, 'root', 1, 2, 'createdAt')
410 expect(res.body.total).to.equal(2)
412 const data: VideoPlaylist[] = res.body.data
413 expect(data).to.have.lengthOf(1)
414 expect(data[0].displayName).to.equal('playlist 3')
418 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '3')
420 expect(res.body.total).to.equal(1)
422 const data: VideoPlaylist[] = res.body.data
423 expect(data).to.have.lengthOf(1)
424 expect(data[0].displayName).to.equal('playlist 3')
428 const res = await getAccountPlaylistsList(servers[1].url, 'root', 0, 10, 'createdAt', '4')
430 expect(res.body.total).to.equal(0)
432 const data: VideoPlaylist[] = res.body.data
433 expect(data).to.have.lengthOf(0)
437 it('Should not list unlisted or private playlists', async function () {
440 await createVideoPlaylist({
442 token: servers[1].accessToken,
444 displayName: 'playlist unlisted',
445 privacy: VideoPlaylistPrivacy.UNLISTED
449 await createVideoPlaylist({
451 token: servers[1].accessToken,
453 displayName: 'playlist private',
454 privacy: VideoPlaylistPrivacy.PRIVATE
458 await waitJobs(servers)
461 for (const server of servers) {
463 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
464 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
467 expect(results[0].body.total).to.equal(2)
468 expect(results[1].body.total).to.equal(3)
470 for (const res of results) {
471 const data: VideoPlaylist[] = res.body.data
472 expect(data).to.have.lengthOf(2)
473 expect(data[0].displayName).to.equal('playlist 3')
474 expect(data[1].displayName).to.equal('playlist 2')
480 describe('Update playlists', function () {
482 it('Should update a playlist', async function () {
485 await updateVideoPlaylist({
487 token: servers[1].accessToken,
489 displayName: 'playlist 3 updated',
490 description: 'description updated',
491 privacy: VideoPlaylistPrivacy.UNLISTED,
492 thumbnailfile: 'thumbnail.jpg',
493 videoChannelId: servers[1].videoChannel.id
495 playlistId: playlistServer2Id2
498 await waitJobs(servers)
500 for (const server of servers) {
501 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
502 const playlist: VideoPlaylist = res.body
504 expect(playlist.displayName).to.equal('playlist 3 updated')
505 expect(playlist.description).to.equal('description updated')
507 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
508 expect(playlist.privacy.label).to.equal('Unlisted')
510 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
511 expect(playlist.type.label).to.equal('Regular')
513 expect(playlist.videosLength).to.equal(2)
515 expect(playlist.ownerAccount.name).to.equal('root')
516 expect(playlist.ownerAccount.displayName).to.equal('root')
517 expect(playlist.videoChannel.name).to.equal('root_channel')
518 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
523 describe('Element timestamps', function () {
525 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
528 const addVideo = (elementAttrs: any) => {
529 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
532 const res = await createVideoPlaylist({
534 token: servers[0].accessToken,
536 displayName: 'playlist 4',
537 privacy: VideoPlaylistPrivacy.PUBLIC,
538 videoChannelId: servers[0].videoChannel.id
542 playlistServer1Id = res.body.videoPlaylist.id
543 playlistServer1UUID = res.body.videoPlaylist.uuid
545 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
546 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
547 await addVideo({ videoId: servers[2].videos[2].uuid })
549 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
550 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
554 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
555 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
559 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
560 playlistElementNSFW = res.body.videoPlaylistElement.id
562 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
563 await addVideo({ videoId: nsfwVideoServer1 })
566 await waitJobs(servers)
569 it('Should correctly list playlist videos', async function () {
572 for (const server of servers) {
573 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
575 expect(res.body.total).to.equal(8)
577 const videoElements: VideoPlaylistElement[] = res.body.data
578 expect(videoElements).to.have.lengthOf(8)
580 expect(videoElements[0].video.name).to.equal('video 0 server 1')
581 expect(videoElements[0].position).to.equal(1)
582 expect(videoElements[0].startTimestamp).to.equal(15)
583 expect(videoElements[0].stopTimestamp).to.equal(28)
585 expect(videoElements[1].video.name).to.equal('video 1 server 3')
586 expect(videoElements[1].position).to.equal(2)
587 expect(videoElements[1].startTimestamp).to.equal(35)
588 expect(videoElements[1].stopTimestamp).to.be.null
590 expect(videoElements[2].video.name).to.equal('video 2 server 3')
591 expect(videoElements[2].position).to.equal(3)
592 expect(videoElements[2].startTimestamp).to.be.null
593 expect(videoElements[2].stopTimestamp).to.be.null
595 expect(videoElements[3].video.name).to.equal('video 3 server 1')
596 expect(videoElements[3].position).to.equal(4)
597 expect(videoElements[3].startTimestamp).to.be.null
598 expect(videoElements[3].stopTimestamp).to.equal(35)
600 expect(videoElements[4].video.name).to.equal('video 4 server 1')
601 expect(videoElements[4].position).to.equal(5)
602 expect(videoElements[4].startTimestamp).to.equal(45)
603 expect(videoElements[4].stopTimestamp).to.equal(60)
605 expect(videoElements[5].video.name).to.equal('NSFW video')
606 expect(videoElements[5].position).to.equal(6)
607 expect(videoElements[5].startTimestamp).to.equal(5)
608 expect(videoElements[5].stopTimestamp).to.be.null
610 expect(videoElements[6].video.name).to.equal('NSFW video')
611 expect(videoElements[6].position).to.equal(7)
612 expect(videoElements[6].startTimestamp).to.equal(4)
613 expect(videoElements[6].stopTimestamp).to.be.null
615 expect(videoElements[7].video.name).to.equal('NSFW video')
616 expect(videoElements[7].position).to.equal(8)
617 expect(videoElements[7].startTimestamp).to.be.null
618 expect(videoElements[7].stopTimestamp).to.be.null
620 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
621 expect(res3.body.data).to.have.lengthOf(2)
626 describe('Element type', function () {
627 let groupUser1: ServerInfo[]
628 let groupWithoutToken1: ServerInfo[]
629 let group1: ServerInfo[]
630 let group2: ServerInfo[]
636 before(async function () {
639 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
640 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
641 group1 = [ servers[0] ]
642 group2 = [ servers[1], servers[2] ]
644 const res = await createVideoPlaylist({
646 token: userAccessTokenServer1,
648 displayName: 'playlist 56',
649 privacy: VideoPlaylistPrivacy.PUBLIC,
650 videoChannelId: servers[0].videoChannel.id
654 const playlistServer1Id2 = res.body.videoPlaylist.id
655 playlistServer1UUID2 = res.body.videoPlaylist.uuid
657 const addVideo = (elementAttrs: any) => {
658 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
661 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
662 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
663 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
665 await waitJobs(servers)
667 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
668 await addVideo({ videoId: video2, startTimestamp: 35 })
669 await addVideo({ videoId: video3 })
671 await waitJobs(servers)
674 it('Should update the element type if the video is private', async function () {
677 const name = 'video 89'
681 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
682 await waitJobs(servers)
684 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
685 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
686 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
687 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
691 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
692 await waitJobs(servers)
694 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
695 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
696 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
697 // We deleted the video, so even if we recreated it, the old entry is still deleted
698 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
702 it('Should update the element type if the video is blacklisted', async function () {
705 const name = 'video 89'
709 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
710 await waitJobs(servers)
712 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
713 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
714 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
715 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
719 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
720 await waitJobs(servers)
722 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
723 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
724 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
725 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
726 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
730 it('Should update the element type if the account or server of the video is blocked', async function () {
733 const name = 'video 90'
737 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
738 await waitJobs(servers)
740 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
741 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
743 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
744 await waitJobs(servers)
746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
750 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
751 await waitJobs(servers)
753 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
754 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
756 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
757 await waitJobs(servers)
759 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
763 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
764 await waitJobs(servers)
766 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
767 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
769 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
770 await waitJobs(servers)
772 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
776 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
777 await waitJobs(servers)
779 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
780 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
782 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
783 await waitJobs(servers)
785 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
789 it('Should hide the video if it is NSFW', async function () {
790 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
791 expect(res.body.total).to.equal(3)
793 const elements: VideoPlaylistElement[] = res.body.data
794 const element = elements.find(e => e.position === 3)
796 expect(element).to.exist
797 expect(element.video).to.be.null
798 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
803 describe('Managing playlist elements', function () {
805 it('Should reorder the playlist', async function () {
809 await reorderVideosPlaylist({
811 token: servers[0].accessToken,
812 playlistId: playlistServer1Id,
815 insertAfterPosition: 3
819 await waitJobs(servers)
821 for (const server of servers) {
822 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
823 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
825 expect(names).to.deep.equal([
839 await reorderVideosPlaylist({
841 token: servers[0].accessToken,
842 playlistId: playlistServer1Id,
846 insertAfterPosition: 4
850 await waitJobs(servers)
852 for (const server of servers) {
853 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
854 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
856 expect(names).to.deep.equal([
870 await reorderVideosPlaylist({
872 token: servers[0].accessToken,
873 playlistId: playlistServer1Id,
876 insertAfterPosition: 3
880 await waitJobs(servers)
882 for (const server of servers) {
883 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
884 const elements: VideoPlaylistElement[] = res.body.data
885 const names = elements.map(v => v.video.name)
887 expect(names).to.deep.equal([
898 for (let i = 1; i <= elements.length; i++) {
899 expect(elements[i - 1].position).to.equal(i)
905 it('Should update startTimestamp/endTimestamp of some elements', async function () {
908 await updateVideoPlaylistElement({
910 token: servers[0].accessToken,
911 playlistId: playlistServer1Id,
912 playlistElementId: playlistElementServer1Video4,
918 await updateVideoPlaylistElement({
920 token: servers[0].accessToken,
921 playlistId: playlistServer1Id,
922 playlistElementId: playlistElementServer1Video5,
928 await waitJobs(servers)
930 for (const server of servers) {
931 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
932 const elements: VideoPlaylistElement[] = res.body.data
934 expect(elements[0].video.name).to.equal('video 3 server 1')
935 expect(elements[0].position).to.equal(1)
936 expect(elements[0].startTimestamp).to.equal(1)
937 expect(elements[0].stopTimestamp).to.equal(35)
939 expect(elements[5].video.name).to.equal('video 4 server 1')
940 expect(elements[5].position).to.equal(6)
941 expect(elements[5].startTimestamp).to.equal(45)
942 expect(elements[5].stopTimestamp).to.be.null
946 it('Should check videos existence in my playlist', async function () {
948 servers[0].videos[0].id,
950 servers[0].videos[3].id,
952 servers[0].videos[4].id
954 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
955 const obj = res.body as VideoExistInPlaylist
958 const elem = obj[servers[0].videos[0].id]
959 expect(elem).to.have.lengthOf(1)
960 expect(elem[0].playlistElementId).to.exist
961 expect(elem[0].playlistId).to.equal(playlistServer1Id)
962 expect(elem[0].startTimestamp).to.equal(15)
963 expect(elem[0].stopTimestamp).to.equal(28)
967 const elem = obj[servers[0].videos[3].id]
968 expect(elem).to.have.lengthOf(1)
969 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
970 expect(elem[0].playlistId).to.equal(playlistServer1Id)
971 expect(elem[0].startTimestamp).to.equal(1)
972 expect(elem[0].stopTimestamp).to.equal(35)
976 const elem = obj[servers[0].videos[4].id]
977 expect(elem).to.have.lengthOf(1)
978 expect(elem[0].playlistId).to.equal(playlistServer1Id)
979 expect(elem[0].startTimestamp).to.equal(45)
980 expect(elem[0].stopTimestamp).to.equal(null)
983 expect(obj[42000]).to.have.lengthOf(0)
984 expect(obj[43000]).to.have.lengthOf(0)
987 it('Should automatically update updatedAt field of playlists', async function () {
988 const server = servers[1]
989 const videoId = servers[1].videos[5].id
991 async function getPlaylistNames () {
992 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
994 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
997 const elementAttrs = { videoId }
998 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
999 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
1001 const element1 = res1.body.videoPlaylistElement.id
1002 const element2 = res2.body.videoPlaylistElement.id
1004 const names1 = await getPlaylistNames()
1005 expect(names1[0]).to.equal('playlist 3 updated')
1006 expect(names1[1]).to.equal('playlist 2')
1008 await removeVideoFromPlaylist({
1010 token: server.accessToken,
1011 playlistId: playlistServer2Id1,
1012 playlistElementId: element1
1015 const names2 = await getPlaylistNames()
1016 expect(names2[0]).to.equal('playlist 2')
1017 expect(names2[1]).to.equal('playlist 3 updated')
1019 await removeVideoFromPlaylist({
1021 token: server.accessToken,
1022 playlistId: playlistServer2Id2,
1023 playlistElementId: element2
1026 const names3 = await getPlaylistNames()
1027 expect(names3[0]).to.equal('playlist 3 updated')
1028 expect(names3[1]).to.equal('playlist 2')
1031 it('Should delete some elements', async function () {
1034 await removeVideoFromPlaylist({
1035 url: servers[0].url,
1036 token: servers[0].accessToken,
1037 playlistId: playlistServer1Id,
1038 playlistElementId: playlistElementServer1Video4
1041 await removeVideoFromPlaylist({
1042 url: servers[0].url,
1043 token: servers[0].accessToken,
1044 playlistId: playlistServer1Id,
1045 playlistElementId: playlistElementNSFW
1048 await waitJobs(servers)
1050 for (const server of servers) {
1051 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1053 expect(res.body.total).to.equal(6)
1055 const elements: VideoPlaylistElement[] = res.body.data
1056 expect(elements).to.have.lengthOf(6)
1058 expect(elements[0].video.name).to.equal('video 0 server 1')
1059 expect(elements[0].position).to.equal(1)
1061 expect(elements[1].video.name).to.equal('video 2 server 3')
1062 expect(elements[1].position).to.equal(2)
1064 expect(elements[2].video.name).to.equal('video 1 server 3')
1065 expect(elements[2].position).to.equal(3)
1067 expect(elements[3].video.name).to.equal('video 4 server 1')
1068 expect(elements[3].position).to.equal(4)
1070 expect(elements[4].video.name).to.equal('NSFW video')
1071 expect(elements[4].position).to.equal(5)
1073 expect(elements[5].video.name).to.equal('NSFW video')
1074 expect(elements[5].position).to.equal(6)
1078 it('Should be able to create a public playlist, and set it to private', async function () {
1081 const res = await createVideoPlaylist({
1082 url: servers[0].url,
1083 token: servers[0].accessToken,
1085 displayName: 'my super public playlist',
1086 privacy: VideoPlaylistPrivacy.PUBLIC,
1087 videoChannelId: servers[0].videoChannel.id
1090 const videoPlaylistIds = res.body.videoPlaylist
1092 await waitJobs(servers)
1094 for (const server of servers) {
1095 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1098 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1099 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1101 await waitJobs(servers)
1103 for (const server of [ servers[1], servers[2] ]) {
1104 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404)
1106 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1108 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1112 describe('Playlist deletion', function () {
1114 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1117 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1119 await waitJobs(servers)
1121 for (const server of servers) {
1122 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404)
1126 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1129 for (const server of servers) {
1130 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1134 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1137 const finder = data => data.find(p => p.displayName === 'my super playlist')
1140 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1141 expect(res.body.total).to.equal(3)
1142 expect(finder(res.body.data)).to.not.be.undefined
1145 await unfollow(servers[2].url, servers[2].accessToken, servers[0])
1148 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1149 expect(res.body.total).to.equal(1)
1151 expect(finder(res.body.data)).to.be.undefined
1155 it('Should delete a channel and put the associated playlist in private mode', async function () {
1158 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1159 const videoChannelId = res.body.videoChannel.id
1161 const res2 = await createVideoPlaylist({
1162 url: servers[0].url,
1163 token: servers[0].accessToken,
1165 displayName: 'channel playlist',
1166 privacy: VideoPlaylistPrivacy.PUBLIC,
1170 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1172 await waitJobs(servers)
1174 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1176 await waitJobs(servers)
1178 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1179 expect(res3.body.displayName).to.equal('channel playlist')
1180 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1182 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404)
1185 it('Should delete an account and delete its playlists', async function () {
1188 const user = { username: 'user_1', password: 'password' }
1189 const res = await createUser({
1190 url: servers[0].url,
1191 accessToken: servers[0].accessToken,
1192 username: user.username,
1193 password: user.password
1196 const userId = res.body.user.id
1197 const userAccessToken = await userLogin(servers[0], user)
1199 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1200 const userChannel = (resChannel.body as User).videoChannels[0]
1202 await createVideoPlaylist({
1203 url: servers[0].url,
1204 token: userAccessToken,
1206 displayName: 'playlist to be deleted',
1207 privacy: VideoPlaylistPrivacy.PUBLIC,
1208 videoChannelId: userChannel.id
1212 await waitJobs(servers)
1214 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1217 for (const server of [ servers[0], servers[1] ]) {
1218 const res = await getVideoPlaylistsList(server.url, 0, 15)
1219 expect(finder(res.body.data)).to.not.be.undefined
1223 await removeUser(servers[0].url, userId, servers[0].accessToken)
1224 await waitJobs(servers)
1227 for (const server of [ servers[0], servers[1] ]) {
1228 const res = await getVideoPlaylistsList(server.url, 0, 15)
1229 expect(finder(res.body.data)).to.be.undefined
1235 after(async function () {
1236 await cleanupTests(servers)