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'
8 checkPlaylistFilesWereRemoved,
13 flushAndRunMultipleServers,
14 generateUserAccessToken,
20 setAccessTokensToServers,
21 setDefaultVideoChannel,
29 } from '@shared/extra-utils'
33 VideoPlaylistCreateResult,
34 VideoPlaylistElementType,
38 } from '@shared/models'
40 const expect = chai.expect
42 async function checkPlaylistElementType (
43 servers: ServerInfo[],
45 type: VideoPlaylistElementType,
50 for (const server of servers) {
51 const body = await server.playlistsCommand.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
52 expect(body.total).to.equal(total)
54 const videoElement = body.data.find(e => e.position === position)
55 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
57 if (type === VideoPlaylistElementType.REGULAR) {
58 expect(videoElement.video).to.not.be.null
59 expect(videoElement.video.name).to.equal(name)
61 expect(videoElement.video).to.be.null
66 describe('Test video playlists', function () {
67 let servers: ServerInfo[] = []
69 let playlistServer2Id1: number
70 let playlistServer2Id2: number
71 let playlistServer2UUID2: string
73 let playlistServer1Id: number
74 let playlistServer1UUID: string
75 let playlistServer1UUID2: string
77 let playlistElementServer1Video4: number
78 let playlistElementServer1Video5: number
79 let playlistElementNSFW: number
81 let nsfwVideoServer1: number
83 let userTokenServer1: string
85 let commands: PlaylistsCommand[]
87 before(async function () {
90 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
92 // Get the access tokens
93 await setAccessTokensToServers(servers)
94 await setDefaultVideoChannel(servers)
96 // Server 1 and server 2 follow each other
97 await doubleFollow(servers[0], servers[1])
98 // Server 1 and server 3 follow each other
99 await doubleFollow(servers[0], servers[2])
101 commands = servers.map(s => s.playlistsCommand)
104 servers[0].videos = []
105 servers[1].videos = []
106 servers[2].videos = []
108 for (const server of servers) {
109 for (let i = 0; i < 7; i++) {
110 const name = `video ${i} server ${server.serverNumber}`
111 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
113 server.videos.push(resVideo.body.video)
118 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
123 accessToken: servers[0].accessToken,
127 userTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
130 await waitJobs(servers)
133 describe('Get default playlists', function () {
135 it('Should list video playlist privacies', async function () {
136 const privacies = await commands[0].getPrivacies()
138 expect(Object.keys(privacies)).to.have.length.at.least(3)
139 expect(privacies[3]).to.equal('Private')
142 it('Should list watch later playlist', async function () {
143 const token = servers[0].accessToken
146 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
148 expect(body.total).to.equal(1)
149 expect(body.data).to.have.lengthOf(1)
151 const playlist = body.data[0]
152 expect(playlist.displayName).to.equal('Watch later')
153 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
154 expect(playlist.type.label).to.equal('Watch later')
158 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
160 expect(body.total).to.equal(0)
161 expect(body.data).to.have.lengthOf(0)
165 const body = await commands[0].listByAccount({ handle: 'root' })
166 expect(body.total).to.equal(0)
167 expect(body.data).to.have.lengthOf(0)
171 it('Should get private playlist for a classic user', async function () {
172 const token = await generateUserAccessToken(servers[0], 'toto')
174 const body = await commands[0].listByAccount({ token, handle: 'toto' })
176 expect(body.total).to.equal(1)
177 expect(body.data).to.have.lengthOf(1)
179 const playlistId = body.data[0].id
180 await commands[0].listVideos({ token, playlistId })
184 describe('Create and federate playlists', function () {
186 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
189 await commands[0].create({
191 displayName: 'my super playlist',
192 privacy: VideoPlaylistPrivacy.PUBLIC,
193 description: 'my super description',
194 thumbnailfile: 'thumbnail.jpg',
195 videoChannelId: servers[0].videoChannel.id
199 await waitJobs(servers)
200 // Processing a playlist by the receiver could be long
203 for (const server of servers) {
204 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
205 expect(body.total).to.equal(1)
206 expect(body.data).to.have.lengthOf(1)
208 const playlistFromList = body.data[0]
210 const playlistFromGet = await server.playlistsCommand.get({ playlistId: playlistFromList.uuid })
212 for (const playlist of [ playlistFromGet, playlistFromList ]) {
213 expect(playlist.id).to.be.a('number')
214 expect(playlist.uuid).to.be.a('string')
216 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
218 expect(playlist.displayName).to.equal('my super playlist')
219 expect(playlist.description).to.equal('my super description')
220 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
221 expect(playlist.privacy.label).to.equal('Public')
222 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
223 expect(playlist.type.label).to.equal('Regular')
224 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
226 expect(playlist.videosLength).to.equal(0)
228 expect(playlist.ownerAccount.name).to.equal('root')
229 expect(playlist.ownerAccount.displayName).to.equal('root')
230 expect(playlist.videoChannel.name).to.equal('root_channel')
231 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
236 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
240 const playlist = await servers[1].playlistsCommand.create({
242 displayName: 'playlist 2',
243 privacy: VideoPlaylistPrivacy.PUBLIC,
244 videoChannelId: servers[1].videoChannel.id
247 playlistServer2Id1 = playlist.id
251 const playlist = await servers[1].playlistsCommand.create({
253 displayName: 'playlist 3',
254 privacy: VideoPlaylistPrivacy.PUBLIC,
255 thumbnailfile: 'thumbnail.jpg',
256 videoChannelId: servers[1].videoChannel.id
260 playlistServer2Id2 = playlist.id
261 playlistServer2UUID2 = playlist.uuid
264 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
265 await servers[1].playlistsCommand.addElement({
267 attributes: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
269 await servers[1].playlistsCommand.addElement({
271 attributes: { videoId: servers[1].videos[1].id }
275 await waitJobs(servers)
278 for (const server of [ servers[0], servers[1] ]) {
279 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
281 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
282 expect(playlist2).to.not.be.undefined
283 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
285 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
286 expect(playlist3).to.not.be.undefined
287 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
290 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
291 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
292 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
295 it('Should have the playlist on server 3 after a new follow', async function () {
298 // Server 2 and server 3 follow each other
299 await doubleFollow(servers[1], servers[2])
301 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
303 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
304 expect(playlist2).to.not.be.undefined
305 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
307 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
311 describe('List playlists', function () {
313 it('Should correctly list the playlists', async function () {
317 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: 'createdAt' })
318 expect(body.total).to.equal(3)
320 const data = body.data
321 expect(data).to.have.lengthOf(2)
322 expect(data[0].displayName).to.equal('playlist 2')
323 expect(data[1].displayName).to.equal('playlist 3')
327 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: '-createdAt' })
328 expect(body.total).to.equal(3)
330 const data = body.data
331 expect(data).to.have.lengthOf(2)
332 expect(data[0].displayName).to.equal('playlist 2')
333 expect(data[1].displayName).to.equal('my super playlist')
337 it('Should list video channel playlists', async function () {
341 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
342 expect(body.total).to.equal(1)
344 const data = body.data
345 expect(data).to.have.lengthOf(1)
346 expect(data[0].displayName).to.equal('my super playlist')
350 it('Should list account playlists', async function () {
354 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
355 expect(body.total).to.equal(2)
357 const data = body.data
358 expect(data).to.have.lengthOf(1)
359 expect(data[0].displayName).to.equal('playlist 2')
363 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
364 expect(body.total).to.equal(2)
366 const data = body.data
367 expect(data).to.have.lengthOf(1)
368 expect(data[0].displayName).to.equal('playlist 3')
372 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
373 expect(body.total).to.equal(1)
375 const data = body.data
376 expect(data).to.have.lengthOf(1)
377 expect(data[0].displayName).to.equal('playlist 3')
381 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
382 expect(body.total).to.equal(0)
384 const data = body.data
385 expect(data).to.have.lengthOf(0)
390 describe('Playlist rights', function () {
391 let unlistedPlaylist: VideoPlaylistCreateResult
392 let privatePlaylist: VideoPlaylistCreateResult
394 before(async function () {
398 unlistedPlaylist = await servers[1].playlistsCommand.create({
400 displayName: 'playlist unlisted',
401 privacy: VideoPlaylistPrivacy.UNLISTED,
402 videoChannelId: servers[1].videoChannel.id
408 privatePlaylist = await servers[1].playlistsCommand.create({
410 displayName: 'playlist private',
411 privacy: VideoPlaylistPrivacy.PRIVATE
416 await waitJobs(servers)
420 it('Should not list unlisted or private playlists', async function () {
421 for (const server of servers) {
423 await server.playlistsCommand.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
424 await server.playlistsCommand.list({ start: 0, count: 2, sort: '-createdAt' })
427 expect(results[0].total).to.equal(2)
428 expect(results[1].total).to.equal(3)
430 for (const body of results) {
431 const data = body.data
432 expect(data).to.have.lengthOf(2)
433 expect(data[0].displayName).to.equal('playlist 3')
434 expect(data[1].displayName).to.equal('playlist 2')
439 it('Should not get unlisted playlist using only the id', async function () {
440 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
443 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
444 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.uuid })
445 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.shortUUID })
448 it('Should not get private playlist without token', async function () {
449 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
450 await servers[1].playlistsCommand.get({ playlistId: id, expectedStatus: 401 })
454 it('Should get private playlist with a token', async function () {
455 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
456 await servers[1].playlistsCommand.get({ token: servers[1].accessToken, playlistId: id })
461 describe('Update playlists', function () {
463 it('Should update a playlist', async function () {
466 await servers[1].playlistsCommand.update({
468 displayName: 'playlist 3 updated',
469 description: 'description updated',
470 privacy: VideoPlaylistPrivacy.UNLISTED,
471 thumbnailfile: 'thumbnail.jpg',
472 videoChannelId: servers[1].videoChannel.id
474 playlistId: playlistServer2Id2
477 await waitJobs(servers)
479 for (const server of servers) {
480 const playlist = await server.playlistsCommand.get({ playlistId: playlistServer2UUID2 })
482 expect(playlist.displayName).to.equal('playlist 3 updated')
483 expect(playlist.description).to.equal('description updated')
485 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
486 expect(playlist.privacy.label).to.equal('Unlisted')
488 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
489 expect(playlist.type.label).to.equal('Regular')
491 expect(playlist.videosLength).to.equal(2)
493 expect(playlist.ownerAccount.name).to.equal('root')
494 expect(playlist.ownerAccount.displayName).to.equal('root')
495 expect(playlist.videoChannel.name).to.equal('root_channel')
496 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
501 describe('Element timestamps', function () {
503 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
506 const addVideo = (attributes: any) => {
507 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
510 const playlist = await commands[0].create({
512 displayName: 'playlist 4',
513 privacy: VideoPlaylistPrivacy.PUBLIC,
514 videoChannelId: servers[0].videoChannel.id
518 playlistServer1Id = playlist.id
519 playlistServer1UUID = playlist.uuid
521 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
522 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
523 await addVideo({ videoId: servers[2].videos[2].uuid })
525 const element = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
526 playlistElementServer1Video4 = element.id
530 const element = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
531 playlistElementServer1Video5 = element.id
535 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
536 playlistElementNSFW = element.id
538 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
539 await addVideo({ videoId: nsfwVideoServer1 })
542 await waitJobs(servers)
545 it('Should correctly list playlist videos', async function () {
548 for (const server of servers) {
550 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
552 expect(body.total).to.equal(8)
554 const videoElements = body.data
555 expect(videoElements).to.have.lengthOf(8)
557 expect(videoElements[0].video.name).to.equal('video 0 server 1')
558 expect(videoElements[0].position).to.equal(1)
559 expect(videoElements[0].startTimestamp).to.equal(15)
560 expect(videoElements[0].stopTimestamp).to.equal(28)
562 expect(videoElements[1].video.name).to.equal('video 1 server 3')
563 expect(videoElements[1].position).to.equal(2)
564 expect(videoElements[1].startTimestamp).to.equal(35)
565 expect(videoElements[1].stopTimestamp).to.be.null
567 expect(videoElements[2].video.name).to.equal('video 2 server 3')
568 expect(videoElements[2].position).to.equal(3)
569 expect(videoElements[2].startTimestamp).to.be.null
570 expect(videoElements[2].stopTimestamp).to.be.null
572 expect(videoElements[3].video.name).to.equal('video 3 server 1')
573 expect(videoElements[3].position).to.equal(4)
574 expect(videoElements[3].startTimestamp).to.be.null
575 expect(videoElements[3].stopTimestamp).to.equal(35)
577 expect(videoElements[4].video.name).to.equal('video 4 server 1')
578 expect(videoElements[4].position).to.equal(5)
579 expect(videoElements[4].startTimestamp).to.equal(45)
580 expect(videoElements[4].stopTimestamp).to.equal(60)
582 expect(videoElements[5].video.name).to.equal('NSFW video')
583 expect(videoElements[5].position).to.equal(6)
584 expect(videoElements[5].startTimestamp).to.equal(5)
585 expect(videoElements[5].stopTimestamp).to.be.null
587 expect(videoElements[6].video.name).to.equal('NSFW video')
588 expect(videoElements[6].position).to.equal(7)
589 expect(videoElements[6].startTimestamp).to.equal(4)
590 expect(videoElements[6].stopTimestamp).to.be.null
592 expect(videoElements[7].video.name).to.equal('NSFW video')
593 expect(videoElements[7].position).to.equal(8)
594 expect(videoElements[7].startTimestamp).to.be.null
595 expect(videoElements[7].stopTimestamp).to.be.null
599 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
600 expect(body.data).to.have.lengthOf(2)
606 describe('Element type', function () {
607 let groupUser1: ServerInfo[]
608 let groupWithoutToken1: ServerInfo[]
609 let group1: ServerInfo[]
610 let group2: ServerInfo[]
616 before(async function () {
619 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
620 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
621 group1 = [ servers[0] ]
622 group2 = [ servers[1], servers[2] ]
624 const playlist = await commands[0].create({
625 token: userTokenServer1,
627 displayName: 'playlist 56',
628 privacy: VideoPlaylistPrivacy.PUBLIC,
629 videoChannelId: servers[0].videoChannel.id
633 const playlistServer1Id2 = playlist.id
634 playlistServer1UUID2 = playlist.uuid
636 const addVideo = (attributes: any) => {
637 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
640 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userTokenServer1 })).uuid
641 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
642 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
644 await waitJobs(servers)
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 servers[0].blacklistCommand.add({ videoId: video1, reason: 'reason', unfederate: 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 servers[0].blacklistCommand.remove({ videoId: 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 command = servers[0].blocklistCommand
714 const name = 'video 90'
718 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
719 await waitJobs(servers)
721 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
722 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
724 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
725 await waitJobs(servers)
727 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
731 await command.addToMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
732 await waitJobs(servers)
734 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
735 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
737 await command.removeFromMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
738 await waitJobs(servers)
740 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
744 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
745 await waitJobs(servers)
747 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
748 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
750 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
751 await waitJobs(servers)
753 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
757 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
758 await waitJobs(servers)
760 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
761 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
763 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
764 await waitJobs(servers)
766 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
770 it('Should hide the video if it is NSFW', async function () {
771 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
772 expect(body.total).to.equal(3)
774 const elements = body.data
775 const element = elements.find(e => e.position === 3)
777 expect(element).to.exist
778 expect(element.video).to.be.null
779 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
784 describe('Managing playlist elements', function () {
786 it('Should reorder the playlist', async function () {
790 await commands[0].reorderElements({
791 playlistId: playlistServer1Id,
794 insertAfterPosition: 3
798 await waitJobs(servers)
800 for (const server of servers) {
801 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
802 const names = body.data.map(v => v.video.name)
804 expect(names).to.deep.equal([
818 await commands[0].reorderElements({
819 playlistId: playlistServer1Id,
823 insertAfterPosition: 4
827 await waitJobs(servers)
829 for (const server of servers) {
830 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
831 const names = body.data.map(v => v.video.name)
833 expect(names).to.deep.equal([
847 await commands[0].reorderElements({
848 playlistId: playlistServer1Id,
851 insertAfterPosition: 3
855 await waitJobs(servers)
857 for (const server of servers) {
858 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
859 const names = elements.map(v => v.video.name)
861 expect(names).to.deep.equal([
872 for (let i = 1; i <= elements.length; i++) {
873 expect(elements[i - 1].position).to.equal(i)
879 it('Should update startTimestamp/endTimestamp of some elements', async function () {
882 await commands[0].updateElement({
883 playlistId: playlistServer1Id,
884 elementId: playlistElementServer1Video4,
890 await commands[0].updateElement({
891 playlistId: playlistServer1Id,
892 elementId: playlistElementServer1Video5,
898 await waitJobs(servers)
900 for (const server of servers) {
901 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
903 expect(elements[0].video.name).to.equal('video 3 server 1')
904 expect(elements[0].position).to.equal(1)
905 expect(elements[0].startTimestamp).to.equal(1)
906 expect(elements[0].stopTimestamp).to.equal(35)
908 expect(elements[5].video.name).to.equal('video 4 server 1')
909 expect(elements[5].position).to.equal(6)
910 expect(elements[5].startTimestamp).to.equal(45)
911 expect(elements[5].stopTimestamp).to.be.null
915 it('Should check videos existence in my playlist', async function () {
917 servers[0].videos[0].id,
919 servers[0].videos[3].id,
921 servers[0].videos[4].id
923 const obj = await commands[0].videosExist({ videoIds })
926 const elem = obj[servers[0].videos[0].id]
927 expect(elem).to.have.lengthOf(1)
928 expect(elem[0].playlistElementId).to.exist
929 expect(elem[0].playlistId).to.equal(playlistServer1Id)
930 expect(elem[0].startTimestamp).to.equal(15)
931 expect(elem[0].stopTimestamp).to.equal(28)
935 const elem = obj[servers[0].videos[3].id]
936 expect(elem).to.have.lengthOf(1)
937 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
938 expect(elem[0].playlistId).to.equal(playlistServer1Id)
939 expect(elem[0].startTimestamp).to.equal(1)
940 expect(elem[0].stopTimestamp).to.equal(35)
944 const elem = obj[servers[0].videos[4].id]
945 expect(elem).to.have.lengthOf(1)
946 expect(elem[0].playlistId).to.equal(playlistServer1Id)
947 expect(elem[0].startTimestamp).to.equal(45)
948 expect(elem[0].stopTimestamp).to.equal(null)
951 expect(obj[42000]).to.have.lengthOf(0)
952 expect(obj[43000]).to.have.lengthOf(0)
955 it('Should automatically update updatedAt field of playlists', async function () {
956 const server = servers[1]
957 const videoId = servers[1].videos[5].id
959 async function getPlaylistNames () {
960 const { data } = await server.playlistsCommand.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
962 return data.map(p => p.displayName)
965 const attributes = { videoId }
966 const element1 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id1, attributes })
967 const element2 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id2, attributes })
969 const names1 = await getPlaylistNames()
970 expect(names1[0]).to.equal('playlist 3 updated')
971 expect(names1[1]).to.equal('playlist 2')
973 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
975 const names2 = await getPlaylistNames()
976 expect(names2[0]).to.equal('playlist 2')
977 expect(names2[1]).to.equal('playlist 3 updated')
979 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
981 const names3 = await getPlaylistNames()
982 expect(names3[0]).to.equal('playlist 3 updated')
983 expect(names3[1]).to.equal('playlist 2')
986 it('Should delete some elements', async function () {
989 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
990 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
992 await waitJobs(servers)
994 for (const server of servers) {
995 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
996 expect(body.total).to.equal(6)
998 const elements = body.data
999 expect(elements).to.have.lengthOf(6)
1001 expect(elements[0].video.name).to.equal('video 0 server 1')
1002 expect(elements[0].position).to.equal(1)
1004 expect(elements[1].video.name).to.equal('video 2 server 3')
1005 expect(elements[1].position).to.equal(2)
1007 expect(elements[2].video.name).to.equal('video 1 server 3')
1008 expect(elements[2].position).to.equal(3)
1010 expect(elements[3].video.name).to.equal('video 4 server 1')
1011 expect(elements[3].position).to.equal(4)
1013 expect(elements[4].video.name).to.equal('NSFW video')
1014 expect(elements[4].position).to.equal(5)
1016 expect(elements[5].video.name).to.equal('NSFW video')
1017 expect(elements[5].position).to.equal(6)
1021 it('Should be able to create a public playlist, and set it to private', async function () {
1024 const videoPlaylistIds = await commands[0].create({
1026 displayName: 'my super public playlist',
1027 privacy: VideoPlaylistPrivacy.PUBLIC,
1028 videoChannelId: servers[0].videoChannel.id
1032 await waitJobs(servers)
1034 for (const server of servers) {
1035 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1038 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1039 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1041 await waitJobs(servers)
1043 for (const server of [ servers[1], servers[2] ]) {
1044 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1047 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1048 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1052 describe('Playlist deletion', function () {
1054 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1057 await commands[0].delete({ playlistId: playlistServer1Id })
1059 await waitJobs(servers)
1061 for (const server of servers) {
1062 await server.playlistsCommand.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1066 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1069 for (const server of servers) {
1070 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1074 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1077 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1080 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1081 expect(body.total).to.equal(3)
1083 expect(finder(body.data)).to.not.be.undefined
1086 await servers[2].followsCommand.unfollow({ target: servers[0] })
1089 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1090 expect(body.total).to.equal(1)
1092 expect(finder(body.data)).to.be.undefined
1096 it('Should delete a channel and put the associated playlist in private mode', async function () {
1099 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1100 const videoChannelId = res.body.videoChannel.id
1102 const playlistCreated = await commands[0].create({
1104 displayName: 'channel playlist',
1105 privacy: VideoPlaylistPrivacy.PUBLIC,
1110 await waitJobs(servers)
1112 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1114 await waitJobs(servers)
1116 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1117 expect(body.displayName).to.equal('channel playlist')
1118 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1120 await servers[1].playlistsCommand.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1123 it('Should delete an account and delete its playlists', async function () {
1126 const user = { username: 'user_1', password: 'password' }
1127 const res = await createUser({
1128 url: servers[0].url,
1129 accessToken: servers[0].accessToken,
1130 username: user.username,
1131 password: user.password
1134 const userId = res.body.user.id
1135 const userAccessToken = await userLogin(servers[0], user)
1137 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1138 const userChannel = (resChannel.body as User).videoChannels[0]
1140 await commands[0].create({
1142 displayName: 'playlist to be deleted',
1143 privacy: VideoPlaylistPrivacy.PUBLIC,
1144 videoChannelId: userChannel.id
1148 await waitJobs(servers)
1150 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1153 for (const server of [ servers[0], servers[1] ]) {
1154 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1156 expect(finder(body.data)).to.not.be.undefined
1160 await removeUser(servers[0].url, userId, servers[0].accessToken)
1161 await waitJobs(servers)
1164 for (const server of [ servers[0], servers[1] ]) {
1165 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1167 expect(finder(body.data)).to.be.undefined
1173 after(async function () {
1174 await cleanupTests(servers)