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'
7 checkPlaylistFilesWereRemoved,
11 flushAndRunMultipleServers,
12 generateUserAccessToken,
17 setAccessTokensToServers,
18 setDefaultVideoChannel,
25 } from '@shared/extra-utils'
29 VideoPlaylistCreateResult,
30 VideoPlaylistElementType,
34 } from '@shared/models'
36 const expect = chai.expect
38 async function checkPlaylistElementType (
39 servers: ServerInfo[],
41 type: VideoPlaylistElementType,
46 for (const server of servers) {
47 const body = await server.playlistsCommand.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
48 expect(body.total).to.equal(total)
50 const videoElement = body.data.find(e => e.position === position)
51 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
53 if (type === VideoPlaylistElementType.REGULAR) {
54 expect(videoElement.video).to.not.be.null
55 expect(videoElement.video.name).to.equal(name)
57 expect(videoElement.video).to.be.null
62 describe('Test video playlists', function () {
63 let servers: ServerInfo[] = []
65 let playlistServer2Id1: number
66 let playlistServer2Id2: number
67 let playlistServer2UUID2: string
69 let playlistServer1Id: number
70 let playlistServer1UUID: string
71 let playlistServer1UUID2: string
73 let playlistElementServer1Video4: number
74 let playlistElementServer1Video5: number
75 let playlistElementNSFW: number
77 let nsfwVideoServer1: number
79 let userTokenServer1: string
81 let commands: PlaylistsCommand[]
83 before(async function () {
86 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
88 // Get the access tokens
89 await setAccessTokensToServers(servers)
90 await setDefaultVideoChannel(servers)
92 // Server 1 and server 2 follow each other
93 await doubleFollow(servers[0], servers[1])
94 // Server 1 and server 3 follow each other
95 await doubleFollow(servers[0], servers[2])
97 commands = servers.map(s => s.playlistsCommand)
100 servers[0].videos = []
101 servers[1].videos = []
102 servers[2].videos = []
104 for (const server of servers) {
105 for (let i = 0; i < 7; i++) {
106 const name = `video ${i} server ${server.serverNumber}`
107 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
109 server.videos.push(resVideo.body.video)
114 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
119 accessToken: servers[0].accessToken,
123 userTokenServer1 = await servers[0].loginCommand.getAccessToken('user1', 'password')
126 await waitJobs(servers)
129 describe('Get default playlists', function () {
131 it('Should list video playlist privacies', async function () {
132 const privacies = await commands[0].getPrivacies()
134 expect(Object.keys(privacies)).to.have.length.at.least(3)
135 expect(privacies[3]).to.equal('Private')
138 it('Should list watch later playlist', async function () {
139 const token = servers[0].accessToken
142 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
144 expect(body.total).to.equal(1)
145 expect(body.data).to.have.lengthOf(1)
147 const playlist = body.data[0]
148 expect(playlist.displayName).to.equal('Watch later')
149 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
150 expect(playlist.type.label).to.equal('Watch later')
154 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
156 expect(body.total).to.equal(0)
157 expect(body.data).to.have.lengthOf(0)
161 const body = await commands[0].listByAccount({ handle: 'root' })
162 expect(body.total).to.equal(0)
163 expect(body.data).to.have.lengthOf(0)
167 it('Should get private playlist for a classic user', async function () {
168 const token = await generateUserAccessToken(servers[0], 'toto')
170 const body = await commands[0].listByAccount({ token, handle: 'toto' })
172 expect(body.total).to.equal(1)
173 expect(body.data).to.have.lengthOf(1)
175 const playlistId = body.data[0].id
176 await commands[0].listVideos({ token, playlistId })
180 describe('Create and federate playlists', function () {
182 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
185 await commands[0].create({
187 displayName: 'my super playlist',
188 privacy: VideoPlaylistPrivacy.PUBLIC,
189 description: 'my super description',
190 thumbnailfile: 'thumbnail.jpg',
191 videoChannelId: servers[0].videoChannel.id
195 await waitJobs(servers)
196 // Processing a playlist by the receiver could be long
199 for (const server of servers) {
200 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
201 expect(body.total).to.equal(1)
202 expect(body.data).to.have.lengthOf(1)
204 const playlistFromList = body.data[0]
206 const playlistFromGet = await server.playlistsCommand.get({ playlistId: playlistFromList.uuid })
208 for (const playlist of [ playlistFromGet, playlistFromList ]) {
209 expect(playlist.id).to.be.a('number')
210 expect(playlist.uuid).to.be.a('string')
212 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
214 expect(playlist.displayName).to.equal('my super playlist')
215 expect(playlist.description).to.equal('my super description')
216 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
217 expect(playlist.privacy.label).to.equal('Public')
218 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
219 expect(playlist.type.label).to.equal('Regular')
220 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
222 expect(playlist.videosLength).to.equal(0)
224 expect(playlist.ownerAccount.name).to.equal('root')
225 expect(playlist.ownerAccount.displayName).to.equal('root')
226 expect(playlist.videoChannel.name).to.equal('root_channel')
227 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
232 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
236 const playlist = await servers[1].playlistsCommand.create({
238 displayName: 'playlist 2',
239 privacy: VideoPlaylistPrivacy.PUBLIC,
240 videoChannelId: servers[1].videoChannel.id
243 playlistServer2Id1 = playlist.id
247 const playlist = await servers[1].playlistsCommand.create({
249 displayName: 'playlist 3',
250 privacy: VideoPlaylistPrivacy.PUBLIC,
251 thumbnailfile: 'thumbnail.jpg',
252 videoChannelId: servers[1].videoChannel.id
256 playlistServer2Id2 = playlist.id
257 playlistServer2UUID2 = playlist.uuid
260 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
261 await servers[1].playlistsCommand.addElement({
263 attributes: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
265 await servers[1].playlistsCommand.addElement({
267 attributes: { videoId: servers[1].videos[1].id }
271 await waitJobs(servers)
274 for (const server of [ servers[0], servers[1] ]) {
275 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
277 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
278 expect(playlist2).to.not.be.undefined
279 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
281 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
282 expect(playlist3).to.not.be.undefined
283 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
286 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
287 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
288 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
291 it('Should have the playlist on server 3 after a new follow', async function () {
294 // Server 2 and server 3 follow each other
295 await doubleFollow(servers[1], servers[2])
297 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
299 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
300 expect(playlist2).to.not.be.undefined
301 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
303 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
307 describe('List playlists', function () {
309 it('Should correctly list the playlists', async function () {
313 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: 'createdAt' })
314 expect(body.total).to.equal(3)
316 const data = body.data
317 expect(data).to.have.lengthOf(2)
318 expect(data[0].displayName).to.equal('playlist 2')
319 expect(data[1].displayName).to.equal('playlist 3')
323 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: '-createdAt' })
324 expect(body.total).to.equal(3)
326 const data = body.data
327 expect(data).to.have.lengthOf(2)
328 expect(data[0].displayName).to.equal('playlist 2')
329 expect(data[1].displayName).to.equal('my super playlist')
333 it('Should list video channel playlists', async function () {
337 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
338 expect(body.total).to.equal(1)
340 const data = body.data
341 expect(data).to.have.lengthOf(1)
342 expect(data[0].displayName).to.equal('my super playlist')
346 it('Should list account playlists', async function () {
350 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
351 expect(body.total).to.equal(2)
353 const data = body.data
354 expect(data).to.have.lengthOf(1)
355 expect(data[0].displayName).to.equal('playlist 2')
359 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
360 expect(body.total).to.equal(2)
362 const data = body.data
363 expect(data).to.have.lengthOf(1)
364 expect(data[0].displayName).to.equal('playlist 3')
368 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
369 expect(body.total).to.equal(1)
371 const data = body.data
372 expect(data).to.have.lengthOf(1)
373 expect(data[0].displayName).to.equal('playlist 3')
377 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
378 expect(body.total).to.equal(0)
380 const data = body.data
381 expect(data).to.have.lengthOf(0)
386 describe('Playlist rights', function () {
387 let unlistedPlaylist: VideoPlaylistCreateResult
388 let privatePlaylist: VideoPlaylistCreateResult
390 before(async function () {
394 unlistedPlaylist = await servers[1].playlistsCommand.create({
396 displayName: 'playlist unlisted',
397 privacy: VideoPlaylistPrivacy.UNLISTED,
398 videoChannelId: servers[1].videoChannel.id
404 privatePlaylist = await servers[1].playlistsCommand.create({
406 displayName: 'playlist private',
407 privacy: VideoPlaylistPrivacy.PRIVATE
412 await waitJobs(servers)
416 it('Should not list unlisted or private playlists', async function () {
417 for (const server of servers) {
419 await server.playlistsCommand.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
420 await server.playlistsCommand.list({ start: 0, count: 2, sort: '-createdAt' })
423 expect(results[0].total).to.equal(2)
424 expect(results[1].total).to.equal(3)
426 for (const body of results) {
427 const data = body.data
428 expect(data).to.have.lengthOf(2)
429 expect(data[0].displayName).to.equal('playlist 3')
430 expect(data[1].displayName).to.equal('playlist 2')
435 it('Should not get unlisted playlist using only the id', async function () {
436 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
439 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
440 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.uuid })
441 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.shortUUID })
444 it('Should not get private playlist without token', async function () {
445 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
446 await servers[1].playlistsCommand.get({ playlistId: id, expectedStatus: 401 })
450 it('Should get private playlist with a token', async function () {
451 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
452 await servers[1].playlistsCommand.get({ token: servers[1].accessToken, playlistId: id })
457 describe('Update playlists', function () {
459 it('Should update a playlist', async function () {
462 await servers[1].playlistsCommand.update({
464 displayName: 'playlist 3 updated',
465 description: 'description updated',
466 privacy: VideoPlaylistPrivacy.UNLISTED,
467 thumbnailfile: 'thumbnail.jpg',
468 videoChannelId: servers[1].videoChannel.id
470 playlistId: playlistServer2Id2
473 await waitJobs(servers)
475 for (const server of servers) {
476 const playlist = await server.playlistsCommand.get({ playlistId: playlistServer2UUID2 })
478 expect(playlist.displayName).to.equal('playlist 3 updated')
479 expect(playlist.description).to.equal('description updated')
481 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
482 expect(playlist.privacy.label).to.equal('Unlisted')
484 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
485 expect(playlist.type.label).to.equal('Regular')
487 expect(playlist.videosLength).to.equal(2)
489 expect(playlist.ownerAccount.name).to.equal('root')
490 expect(playlist.ownerAccount.displayName).to.equal('root')
491 expect(playlist.videoChannel.name).to.equal('root_channel')
492 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
497 describe('Element timestamps', function () {
499 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
502 const addVideo = (attributes: any) => {
503 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
506 const playlist = await commands[0].create({
508 displayName: 'playlist 4',
509 privacy: VideoPlaylistPrivacy.PUBLIC,
510 videoChannelId: servers[0].videoChannel.id
514 playlistServer1Id = playlist.id
515 playlistServer1UUID = playlist.uuid
517 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
518 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
519 await addVideo({ videoId: servers[2].videos[2].uuid })
521 const element = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
522 playlistElementServer1Video4 = element.id
526 const element = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
527 playlistElementServer1Video5 = element.id
531 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
532 playlistElementNSFW = element.id
534 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
535 await addVideo({ videoId: nsfwVideoServer1 })
538 await waitJobs(servers)
541 it('Should correctly list playlist videos', async function () {
544 for (const server of servers) {
546 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
548 expect(body.total).to.equal(8)
550 const videoElements = body.data
551 expect(videoElements).to.have.lengthOf(8)
553 expect(videoElements[0].video.name).to.equal('video 0 server 1')
554 expect(videoElements[0].position).to.equal(1)
555 expect(videoElements[0].startTimestamp).to.equal(15)
556 expect(videoElements[0].stopTimestamp).to.equal(28)
558 expect(videoElements[1].video.name).to.equal('video 1 server 3')
559 expect(videoElements[1].position).to.equal(2)
560 expect(videoElements[1].startTimestamp).to.equal(35)
561 expect(videoElements[1].stopTimestamp).to.be.null
563 expect(videoElements[2].video.name).to.equal('video 2 server 3')
564 expect(videoElements[2].position).to.equal(3)
565 expect(videoElements[2].startTimestamp).to.be.null
566 expect(videoElements[2].stopTimestamp).to.be.null
568 expect(videoElements[3].video.name).to.equal('video 3 server 1')
569 expect(videoElements[3].position).to.equal(4)
570 expect(videoElements[3].startTimestamp).to.be.null
571 expect(videoElements[3].stopTimestamp).to.equal(35)
573 expect(videoElements[4].video.name).to.equal('video 4 server 1')
574 expect(videoElements[4].position).to.equal(5)
575 expect(videoElements[4].startTimestamp).to.equal(45)
576 expect(videoElements[4].stopTimestamp).to.equal(60)
578 expect(videoElements[5].video.name).to.equal('NSFW video')
579 expect(videoElements[5].position).to.equal(6)
580 expect(videoElements[5].startTimestamp).to.equal(5)
581 expect(videoElements[5].stopTimestamp).to.be.null
583 expect(videoElements[6].video.name).to.equal('NSFW video')
584 expect(videoElements[6].position).to.equal(7)
585 expect(videoElements[6].startTimestamp).to.equal(4)
586 expect(videoElements[6].stopTimestamp).to.be.null
588 expect(videoElements[7].video.name).to.equal('NSFW video')
589 expect(videoElements[7].position).to.equal(8)
590 expect(videoElements[7].startTimestamp).to.be.null
591 expect(videoElements[7].stopTimestamp).to.be.null
595 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
596 expect(body.data).to.have.lengthOf(2)
602 describe('Element type', function () {
603 let groupUser1: ServerInfo[]
604 let groupWithoutToken1: ServerInfo[]
605 let group1: ServerInfo[]
606 let group2: ServerInfo[]
612 before(async function () {
615 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
616 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
617 group1 = [ servers[0] ]
618 group2 = [ servers[1], servers[2] ]
620 const playlist = await commands[0].create({
621 token: userTokenServer1,
623 displayName: 'playlist 56',
624 privacy: VideoPlaylistPrivacy.PUBLIC,
625 videoChannelId: servers[0].videoChannel.id
629 const playlistServer1Id2 = playlist.id
630 playlistServer1UUID2 = playlist.uuid
632 const addVideo = (attributes: any) => {
633 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
636 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userTokenServer1 })).uuid
637 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
638 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
640 await waitJobs(servers)
642 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
643 await addVideo({ videoId: video2, startTimestamp: 35 })
644 await addVideo({ videoId: video3 })
646 await waitJobs(servers)
649 it('Should update the element type if the video is private', async function () {
652 const name = 'video 89'
656 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
657 await waitJobs(servers)
659 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
660 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
661 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
662 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
666 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
667 await waitJobs(servers)
669 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
670 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
671 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
672 // We deleted the video, so even if we recreated it, the old entry is still deleted
673 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
677 it('Should update the element type if the video is blacklisted', async function () {
680 const name = 'video 89'
684 await servers[0].blacklistCommand.add({ videoId: video1, reason: 'reason', unfederate: true })
685 await waitJobs(servers)
687 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
688 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
689 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
690 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
694 await servers[0].blacklistCommand.remove({ videoId: video1 })
695 await waitJobs(servers)
697 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
698 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
699 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
700 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
701 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
705 it('Should update the element type if the account or server of the video is blocked', async function () {
708 const command = servers[0].blocklistCommand
710 const name = 'video 90'
714 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
715 await waitJobs(servers)
717 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
718 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
720 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
721 await waitJobs(servers)
723 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
727 await command.addToMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
728 await waitJobs(servers)
730 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
731 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
733 await command.removeFromMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
734 await waitJobs(servers)
736 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
740 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
741 await waitJobs(servers)
743 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
744 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
746 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
747 await waitJobs(servers)
749 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
753 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
754 await waitJobs(servers)
756 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
757 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
759 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
760 await waitJobs(servers)
762 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
766 it('Should hide the video if it is NSFW', async function () {
767 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
768 expect(body.total).to.equal(3)
770 const elements = body.data
771 const element = elements.find(e => e.position === 3)
773 expect(element).to.exist
774 expect(element.video).to.be.null
775 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
780 describe('Managing playlist elements', function () {
782 it('Should reorder the playlist', async function () {
786 await commands[0].reorderElements({
787 playlistId: playlistServer1Id,
790 insertAfterPosition: 3
794 await waitJobs(servers)
796 for (const server of servers) {
797 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
798 const names = body.data.map(v => v.video.name)
800 expect(names).to.deep.equal([
814 await commands[0].reorderElements({
815 playlistId: playlistServer1Id,
819 insertAfterPosition: 4
823 await waitJobs(servers)
825 for (const server of servers) {
826 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
827 const names = body.data.map(v => v.video.name)
829 expect(names).to.deep.equal([
843 await commands[0].reorderElements({
844 playlistId: playlistServer1Id,
847 insertAfterPosition: 3
851 await waitJobs(servers)
853 for (const server of servers) {
854 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
855 const names = elements.map(v => v.video.name)
857 expect(names).to.deep.equal([
868 for (let i = 1; i <= elements.length; i++) {
869 expect(elements[i - 1].position).to.equal(i)
875 it('Should update startTimestamp/endTimestamp of some elements', async function () {
878 await commands[0].updateElement({
879 playlistId: playlistServer1Id,
880 elementId: playlistElementServer1Video4,
886 await commands[0].updateElement({
887 playlistId: playlistServer1Id,
888 elementId: playlistElementServer1Video5,
894 await waitJobs(servers)
896 for (const server of servers) {
897 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
899 expect(elements[0].video.name).to.equal('video 3 server 1')
900 expect(elements[0].position).to.equal(1)
901 expect(elements[0].startTimestamp).to.equal(1)
902 expect(elements[0].stopTimestamp).to.equal(35)
904 expect(elements[5].video.name).to.equal('video 4 server 1')
905 expect(elements[5].position).to.equal(6)
906 expect(elements[5].startTimestamp).to.equal(45)
907 expect(elements[5].stopTimestamp).to.be.null
911 it('Should check videos existence in my playlist', async function () {
913 servers[0].videos[0].id,
915 servers[0].videos[3].id,
917 servers[0].videos[4].id
919 const obj = await commands[0].videosExist({ videoIds })
922 const elem = obj[servers[0].videos[0].id]
923 expect(elem).to.have.lengthOf(1)
924 expect(elem[0].playlistElementId).to.exist
925 expect(elem[0].playlistId).to.equal(playlistServer1Id)
926 expect(elem[0].startTimestamp).to.equal(15)
927 expect(elem[0].stopTimestamp).to.equal(28)
931 const elem = obj[servers[0].videos[3].id]
932 expect(elem).to.have.lengthOf(1)
933 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
934 expect(elem[0].playlistId).to.equal(playlistServer1Id)
935 expect(elem[0].startTimestamp).to.equal(1)
936 expect(elem[0].stopTimestamp).to.equal(35)
940 const elem = obj[servers[0].videos[4].id]
941 expect(elem).to.have.lengthOf(1)
942 expect(elem[0].playlistId).to.equal(playlistServer1Id)
943 expect(elem[0].startTimestamp).to.equal(45)
944 expect(elem[0].stopTimestamp).to.equal(null)
947 expect(obj[42000]).to.have.lengthOf(0)
948 expect(obj[43000]).to.have.lengthOf(0)
951 it('Should automatically update updatedAt field of playlists', async function () {
952 const server = servers[1]
953 const videoId = servers[1].videos[5].id
955 async function getPlaylistNames () {
956 const { data } = await server.playlistsCommand.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
958 return data.map(p => p.displayName)
961 const attributes = { videoId }
962 const element1 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id1, attributes })
963 const element2 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id2, attributes })
965 const names1 = await getPlaylistNames()
966 expect(names1[0]).to.equal('playlist 3 updated')
967 expect(names1[1]).to.equal('playlist 2')
969 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
971 const names2 = await getPlaylistNames()
972 expect(names2[0]).to.equal('playlist 2')
973 expect(names2[1]).to.equal('playlist 3 updated')
975 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
977 const names3 = await getPlaylistNames()
978 expect(names3[0]).to.equal('playlist 3 updated')
979 expect(names3[1]).to.equal('playlist 2')
982 it('Should delete some elements', async function () {
985 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
986 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
988 await waitJobs(servers)
990 for (const server of servers) {
991 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
992 expect(body.total).to.equal(6)
994 const elements = body.data
995 expect(elements).to.have.lengthOf(6)
997 expect(elements[0].video.name).to.equal('video 0 server 1')
998 expect(elements[0].position).to.equal(1)
1000 expect(elements[1].video.name).to.equal('video 2 server 3')
1001 expect(elements[1].position).to.equal(2)
1003 expect(elements[2].video.name).to.equal('video 1 server 3')
1004 expect(elements[2].position).to.equal(3)
1006 expect(elements[3].video.name).to.equal('video 4 server 1')
1007 expect(elements[3].position).to.equal(4)
1009 expect(elements[4].video.name).to.equal('NSFW video')
1010 expect(elements[4].position).to.equal(5)
1012 expect(elements[5].video.name).to.equal('NSFW video')
1013 expect(elements[5].position).to.equal(6)
1017 it('Should be able to create a public playlist, and set it to private', async function () {
1020 const videoPlaylistIds = await commands[0].create({
1022 displayName: 'my super public playlist',
1023 privacy: VideoPlaylistPrivacy.PUBLIC,
1024 videoChannelId: servers[0].videoChannel.id
1028 await waitJobs(servers)
1030 for (const server of servers) {
1031 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1034 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1035 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1037 await waitJobs(servers)
1039 for (const server of [ servers[1], servers[2] ]) {
1040 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1043 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1044 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1048 describe('Playlist deletion', function () {
1050 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1053 await commands[0].delete({ playlistId: playlistServer1Id })
1055 await waitJobs(servers)
1057 for (const server of servers) {
1058 await server.playlistsCommand.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1062 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1065 for (const server of servers) {
1066 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1070 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1073 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1076 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1077 expect(body.total).to.equal(3)
1079 expect(finder(body.data)).to.not.be.undefined
1082 await servers[2].followsCommand.unfollow({ target: servers[0] })
1085 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1086 expect(body.total).to.equal(1)
1088 expect(finder(body.data)).to.be.undefined
1092 it('Should delete a channel and put the associated playlist in private mode', async function () {
1095 const channel = await servers[0].channelsCommand.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1097 const playlistCreated = await commands[0].create({
1099 displayName: 'channel playlist',
1100 privacy: VideoPlaylistPrivacy.PUBLIC,
1101 videoChannelId: channel.id
1105 await waitJobs(servers)
1107 await servers[0].channelsCommand.delete({ channelName: 'super_channel' })
1109 await waitJobs(servers)
1111 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1112 expect(body.displayName).to.equal('channel playlist')
1113 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1115 await servers[1].playlistsCommand.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1118 it('Should delete an account and delete its playlists', async function () {
1121 const user = { username: 'user_1', password: 'password' }
1122 const res = await createUser({
1123 url: servers[0].url,
1124 accessToken: servers[0].accessToken,
1125 username: user.username,
1126 password: user.password
1129 const userId = res.body.user.id
1130 const userAccessToken = await servers[0].loginCommand.getAccessToken(user)
1132 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1133 const userChannel = (resChannel.body as User).videoChannels[0]
1135 await commands[0].create({
1137 displayName: 'playlist to be deleted',
1138 privacy: VideoPlaylistPrivacy.PUBLIC,
1139 videoChannelId: userChannel.id
1143 await waitJobs(servers)
1145 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1148 for (const server of [ servers[0], servers[1] ]) {
1149 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1151 expect(finder(body.data)).to.not.be.undefined
1155 await removeUser(servers[0].url, userId, servers[0].accessToken)
1156 await waitJobs(servers)
1159 for (const server of [ servers[0], servers[1] ]) {
1160 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1162 expect(finder(body.data)).to.be.undefined
1168 after(async function () {
1169 await cleanupTests(servers)