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,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
27 } from '@shared/extra-utils'
31 VideoPlaylistCreateResult,
32 VideoPlaylistElementType,
36 } from '@shared/models'
38 const expect = chai.expect
40 async function checkPlaylistElementType (
41 servers: ServerInfo[],
43 type: VideoPlaylistElementType,
48 for (const server of servers) {
49 const body = await server.playlistsCommand.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
50 expect(body.total).to.equal(total)
52 const videoElement = body.data.find(e => e.position === position)
53 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
55 if (type === VideoPlaylistElementType.REGULAR) {
56 expect(videoElement.video).to.not.be.null
57 expect(videoElement.video.name).to.equal(name)
59 expect(videoElement.video).to.be.null
64 describe('Test video playlists', function () {
65 let servers: ServerInfo[] = []
67 let playlistServer2Id1: number
68 let playlistServer2Id2: number
69 let playlistServer2UUID2: string
71 let playlistServer1Id: number
72 let playlistServer1UUID: string
73 let playlistServer1UUID2: string
75 let playlistElementServer1Video4: number
76 let playlistElementServer1Video5: number
77 let playlistElementNSFW: number
79 let nsfwVideoServer1: number
81 let userTokenServer1: string
83 let commands: PlaylistsCommand[]
85 before(async function () {
88 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
90 // Get the access tokens
91 await setAccessTokensToServers(servers)
92 await setDefaultVideoChannel(servers)
94 // Server 1 and server 2 follow each other
95 await doubleFollow(servers[0], servers[1])
96 // Server 1 and server 3 follow each other
97 await doubleFollow(servers[0], servers[2])
99 commands = servers.map(s => s.playlistsCommand)
102 servers[0].videos = []
103 servers[1].videos = []
104 servers[2].videos = []
106 for (const server of servers) {
107 for (let i = 0; i < 7; i++) {
108 const name = `video ${i} server ${server.serverNumber}`
109 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
111 server.videos.push(resVideo.body.video)
116 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
121 accessToken: servers[0].accessToken,
125 userTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
128 await waitJobs(servers)
131 describe('Get default playlists', function () {
133 it('Should list video playlist privacies', async function () {
134 const privacies = await commands[0].getPrivacies()
136 expect(Object.keys(privacies)).to.have.length.at.least(3)
137 expect(privacies[3]).to.equal('Private')
140 it('Should list watch later playlist', async function () {
141 const token = servers[0].accessToken
144 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
146 expect(body.total).to.equal(1)
147 expect(body.data).to.have.lengthOf(1)
149 const playlist = body.data[0]
150 expect(playlist.displayName).to.equal('Watch later')
151 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
152 expect(playlist.type.label).to.equal('Watch later')
156 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
158 expect(body.total).to.equal(0)
159 expect(body.data).to.have.lengthOf(0)
163 const body = await commands[0].listByAccount({ handle: 'root' })
164 expect(body.total).to.equal(0)
165 expect(body.data).to.have.lengthOf(0)
169 it('Should get private playlist for a classic user', async function () {
170 const token = await generateUserAccessToken(servers[0], 'toto')
172 const body = await commands[0].listByAccount({ token, handle: 'toto' })
174 expect(body.total).to.equal(1)
175 expect(body.data).to.have.lengthOf(1)
177 const playlistId = body.data[0].id
178 await commands[0].listVideos({ token, playlistId })
182 describe('Create and federate playlists', function () {
184 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
187 await commands[0].create({
189 displayName: 'my super playlist',
190 privacy: VideoPlaylistPrivacy.PUBLIC,
191 description: 'my super description',
192 thumbnailfile: 'thumbnail.jpg',
193 videoChannelId: servers[0].videoChannel.id
197 await waitJobs(servers)
198 // Processing a playlist by the receiver could be long
201 for (const server of servers) {
202 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
203 expect(body.total).to.equal(1)
204 expect(body.data).to.have.lengthOf(1)
206 const playlistFromList = body.data[0]
208 const playlistFromGet = await server.playlistsCommand.get({ playlistId: playlistFromList.uuid })
210 for (const playlist of [ playlistFromGet, playlistFromList ]) {
211 expect(playlist.id).to.be.a('number')
212 expect(playlist.uuid).to.be.a('string')
214 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
216 expect(playlist.displayName).to.equal('my super playlist')
217 expect(playlist.description).to.equal('my super description')
218 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
219 expect(playlist.privacy.label).to.equal('Public')
220 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
221 expect(playlist.type.label).to.equal('Regular')
222 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
224 expect(playlist.videosLength).to.equal(0)
226 expect(playlist.ownerAccount.name).to.equal('root')
227 expect(playlist.ownerAccount.displayName).to.equal('root')
228 expect(playlist.videoChannel.name).to.equal('root_channel')
229 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
234 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
238 const playlist = await servers[1].playlistsCommand.create({
240 displayName: 'playlist 2',
241 privacy: VideoPlaylistPrivacy.PUBLIC,
242 videoChannelId: servers[1].videoChannel.id
245 playlistServer2Id1 = playlist.id
249 const playlist = await servers[1].playlistsCommand.create({
251 displayName: 'playlist 3',
252 privacy: VideoPlaylistPrivacy.PUBLIC,
253 thumbnailfile: 'thumbnail.jpg',
254 videoChannelId: servers[1].videoChannel.id
258 playlistServer2Id2 = playlist.id
259 playlistServer2UUID2 = playlist.uuid
262 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
263 await servers[1].playlistsCommand.addElement({
265 attributes: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
267 await servers[1].playlistsCommand.addElement({
269 attributes: { videoId: servers[1].videos[1].id }
273 await waitJobs(servers)
276 for (const server of [ servers[0], servers[1] ]) {
277 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
279 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
280 expect(playlist2).to.not.be.undefined
281 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
283 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
284 expect(playlist3).to.not.be.undefined
285 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
288 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
289 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
290 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
293 it('Should have the playlist on server 3 after a new follow', async function () {
296 // Server 2 and server 3 follow each other
297 await doubleFollow(servers[1], servers[2])
299 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
301 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
302 expect(playlist2).to.not.be.undefined
303 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
305 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
309 describe('List playlists', function () {
311 it('Should correctly list the playlists', async function () {
315 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: 'createdAt' })
316 expect(body.total).to.equal(3)
318 const data = body.data
319 expect(data).to.have.lengthOf(2)
320 expect(data[0].displayName).to.equal('playlist 2')
321 expect(data[1].displayName).to.equal('playlist 3')
325 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: '-createdAt' })
326 expect(body.total).to.equal(3)
328 const data = body.data
329 expect(data).to.have.lengthOf(2)
330 expect(data[0].displayName).to.equal('playlist 2')
331 expect(data[1].displayName).to.equal('my super playlist')
335 it('Should list video channel playlists', async function () {
339 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
340 expect(body.total).to.equal(1)
342 const data = body.data
343 expect(data).to.have.lengthOf(1)
344 expect(data[0].displayName).to.equal('my super playlist')
348 it('Should list account playlists', async function () {
352 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
353 expect(body.total).to.equal(2)
355 const data = body.data
356 expect(data).to.have.lengthOf(1)
357 expect(data[0].displayName).to.equal('playlist 2')
361 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
362 expect(body.total).to.equal(2)
364 const data = body.data
365 expect(data).to.have.lengthOf(1)
366 expect(data[0].displayName).to.equal('playlist 3')
370 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
371 expect(body.total).to.equal(1)
373 const data = body.data
374 expect(data).to.have.lengthOf(1)
375 expect(data[0].displayName).to.equal('playlist 3')
379 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
380 expect(body.total).to.equal(0)
382 const data = body.data
383 expect(data).to.have.lengthOf(0)
388 describe('Playlist rights', function () {
389 let unlistedPlaylist: VideoPlaylistCreateResult
390 let privatePlaylist: VideoPlaylistCreateResult
392 before(async function () {
396 unlistedPlaylist = await servers[1].playlistsCommand.create({
398 displayName: 'playlist unlisted',
399 privacy: VideoPlaylistPrivacy.UNLISTED,
400 videoChannelId: servers[1].videoChannel.id
406 privatePlaylist = await servers[1].playlistsCommand.create({
408 displayName: 'playlist private',
409 privacy: VideoPlaylistPrivacy.PRIVATE
414 await waitJobs(servers)
418 it('Should not list unlisted or private playlists', async function () {
419 for (const server of servers) {
421 await server.playlistsCommand.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
422 await server.playlistsCommand.list({ start: 0, count: 2, sort: '-createdAt' })
425 expect(results[0].total).to.equal(2)
426 expect(results[1].total).to.equal(3)
428 for (const body of results) {
429 const data = body.data
430 expect(data).to.have.lengthOf(2)
431 expect(data[0].displayName).to.equal('playlist 3')
432 expect(data[1].displayName).to.equal('playlist 2')
437 it('Should not get unlisted playlist using only the id', async function () {
438 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
441 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
442 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.uuid })
443 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.shortUUID })
446 it('Should not get private playlist without token', async function () {
447 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
448 await servers[1].playlistsCommand.get({ playlistId: id, expectedStatus: 401 })
452 it('Should get private playlist with a token', async function () {
453 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
454 await servers[1].playlistsCommand.get({ token: servers[1].accessToken, playlistId: id })
459 describe('Update playlists', function () {
461 it('Should update a playlist', async function () {
464 await servers[1].playlistsCommand.update({
466 displayName: 'playlist 3 updated',
467 description: 'description updated',
468 privacy: VideoPlaylistPrivacy.UNLISTED,
469 thumbnailfile: 'thumbnail.jpg',
470 videoChannelId: servers[1].videoChannel.id
472 playlistId: playlistServer2Id2
475 await waitJobs(servers)
477 for (const server of servers) {
478 const playlist = await server.playlistsCommand.get({ playlistId: playlistServer2UUID2 })
480 expect(playlist.displayName).to.equal('playlist 3 updated')
481 expect(playlist.description).to.equal('description updated')
483 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
484 expect(playlist.privacy.label).to.equal('Unlisted')
486 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
487 expect(playlist.type.label).to.equal('Regular')
489 expect(playlist.videosLength).to.equal(2)
491 expect(playlist.ownerAccount.name).to.equal('root')
492 expect(playlist.ownerAccount.displayName).to.equal('root')
493 expect(playlist.videoChannel.name).to.equal('root_channel')
494 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
499 describe('Element timestamps', function () {
501 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
504 const addVideo = (attributes: any) => {
505 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
508 const playlist = await commands[0].create({
510 displayName: 'playlist 4',
511 privacy: VideoPlaylistPrivacy.PUBLIC,
512 videoChannelId: servers[0].videoChannel.id
516 playlistServer1Id = playlist.id
517 playlistServer1UUID = playlist.uuid
519 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
520 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
521 await addVideo({ videoId: servers[2].videos[2].uuid })
523 const element = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
524 playlistElementServer1Video4 = element.id
528 const element = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
529 playlistElementServer1Video5 = element.id
533 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
534 playlistElementNSFW = element.id
536 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
537 await addVideo({ videoId: nsfwVideoServer1 })
540 await waitJobs(servers)
543 it('Should correctly list playlist videos', async function () {
546 for (const server of servers) {
548 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
550 expect(body.total).to.equal(8)
552 const videoElements = body.data
553 expect(videoElements).to.have.lengthOf(8)
555 expect(videoElements[0].video.name).to.equal('video 0 server 1')
556 expect(videoElements[0].position).to.equal(1)
557 expect(videoElements[0].startTimestamp).to.equal(15)
558 expect(videoElements[0].stopTimestamp).to.equal(28)
560 expect(videoElements[1].video.name).to.equal('video 1 server 3')
561 expect(videoElements[1].position).to.equal(2)
562 expect(videoElements[1].startTimestamp).to.equal(35)
563 expect(videoElements[1].stopTimestamp).to.be.null
565 expect(videoElements[2].video.name).to.equal('video 2 server 3')
566 expect(videoElements[2].position).to.equal(3)
567 expect(videoElements[2].startTimestamp).to.be.null
568 expect(videoElements[2].stopTimestamp).to.be.null
570 expect(videoElements[3].video.name).to.equal('video 3 server 1')
571 expect(videoElements[3].position).to.equal(4)
572 expect(videoElements[3].startTimestamp).to.be.null
573 expect(videoElements[3].stopTimestamp).to.equal(35)
575 expect(videoElements[4].video.name).to.equal('video 4 server 1')
576 expect(videoElements[4].position).to.equal(5)
577 expect(videoElements[4].startTimestamp).to.equal(45)
578 expect(videoElements[4].stopTimestamp).to.equal(60)
580 expect(videoElements[5].video.name).to.equal('NSFW video')
581 expect(videoElements[5].position).to.equal(6)
582 expect(videoElements[5].startTimestamp).to.equal(5)
583 expect(videoElements[5].stopTimestamp).to.be.null
585 expect(videoElements[6].video.name).to.equal('NSFW video')
586 expect(videoElements[6].position).to.equal(7)
587 expect(videoElements[6].startTimestamp).to.equal(4)
588 expect(videoElements[6].stopTimestamp).to.be.null
590 expect(videoElements[7].video.name).to.equal('NSFW video')
591 expect(videoElements[7].position).to.equal(8)
592 expect(videoElements[7].startTimestamp).to.be.null
593 expect(videoElements[7].stopTimestamp).to.be.null
597 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
598 expect(body.data).to.have.lengthOf(2)
604 describe('Element type', function () {
605 let groupUser1: ServerInfo[]
606 let groupWithoutToken1: ServerInfo[]
607 let group1: ServerInfo[]
608 let group2: ServerInfo[]
614 before(async function () {
617 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
618 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
619 group1 = [ servers[0] ]
620 group2 = [ servers[1], servers[2] ]
622 const playlist = await commands[0].create({
623 token: userTokenServer1,
625 displayName: 'playlist 56',
626 privacy: VideoPlaylistPrivacy.PUBLIC,
627 videoChannelId: servers[0].videoChannel.id
631 const playlistServer1Id2 = playlist.id
632 playlistServer1UUID2 = playlist.uuid
634 const addVideo = (attributes: any) => {
635 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
638 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userTokenServer1 })).uuid
639 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
640 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
642 await waitJobs(servers)
644 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
645 await addVideo({ videoId: video2, startTimestamp: 35 })
646 await addVideo({ videoId: video3 })
648 await waitJobs(servers)
651 it('Should update the element type if the video is private', async function () {
654 const name = 'video 89'
658 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
659 await waitJobs(servers)
661 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
662 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
663 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
664 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
668 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
669 await waitJobs(servers)
671 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
672 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
673 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
674 // We deleted the video, so even if we recreated it, the old entry is still deleted
675 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
679 it('Should update the element type if the video is blacklisted', async function () {
682 const name = 'video 89'
686 await servers[0].blacklistCommand.add({ videoId: video1, reason: 'reason', unfederate: true })
687 await waitJobs(servers)
689 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
690 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
691 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
692 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
696 await servers[0].blacklistCommand.remove({ videoId: video1 })
697 await waitJobs(servers)
699 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
700 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
701 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
702 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
703 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
707 it('Should update the element type if the account or server of the video is blocked', async function () {
710 const command = servers[0].blocklistCommand
712 const name = 'video 90'
716 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
717 await waitJobs(servers)
719 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
720 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
722 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
723 await waitJobs(servers)
725 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
729 await command.addToMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
730 await waitJobs(servers)
732 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
733 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
735 await command.removeFromMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
736 await waitJobs(servers)
738 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
742 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
743 await waitJobs(servers)
745 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
748 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
749 await waitJobs(servers)
751 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
755 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
756 await waitJobs(servers)
758 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
759 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
761 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
762 await waitJobs(servers)
764 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
768 it('Should hide the video if it is NSFW', async function () {
769 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
770 expect(body.total).to.equal(3)
772 const elements = body.data
773 const element = elements.find(e => e.position === 3)
775 expect(element).to.exist
776 expect(element.video).to.be.null
777 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
782 describe('Managing playlist elements', function () {
784 it('Should reorder the playlist', async function () {
788 await commands[0].reorderElements({
789 playlistId: playlistServer1Id,
792 insertAfterPosition: 3
796 await waitJobs(servers)
798 for (const server of servers) {
799 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
800 const names = body.data.map(v => v.video.name)
802 expect(names).to.deep.equal([
816 await commands[0].reorderElements({
817 playlistId: playlistServer1Id,
821 insertAfterPosition: 4
825 await waitJobs(servers)
827 for (const server of servers) {
828 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
829 const names = body.data.map(v => v.video.name)
831 expect(names).to.deep.equal([
845 await commands[0].reorderElements({
846 playlistId: playlistServer1Id,
849 insertAfterPosition: 3
853 await waitJobs(servers)
855 for (const server of servers) {
856 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
857 const names = elements.map(v => v.video.name)
859 expect(names).to.deep.equal([
870 for (let i = 1; i <= elements.length; i++) {
871 expect(elements[i - 1].position).to.equal(i)
877 it('Should update startTimestamp/endTimestamp of some elements', async function () {
880 await commands[0].updateElement({
881 playlistId: playlistServer1Id,
882 elementId: playlistElementServer1Video4,
888 await commands[0].updateElement({
889 playlistId: playlistServer1Id,
890 elementId: playlistElementServer1Video5,
896 await waitJobs(servers)
898 for (const server of servers) {
899 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
901 expect(elements[0].video.name).to.equal('video 3 server 1')
902 expect(elements[0].position).to.equal(1)
903 expect(elements[0].startTimestamp).to.equal(1)
904 expect(elements[0].stopTimestamp).to.equal(35)
906 expect(elements[5].video.name).to.equal('video 4 server 1')
907 expect(elements[5].position).to.equal(6)
908 expect(elements[5].startTimestamp).to.equal(45)
909 expect(elements[5].stopTimestamp).to.be.null
913 it('Should check videos existence in my playlist', async function () {
915 servers[0].videos[0].id,
917 servers[0].videos[3].id,
919 servers[0].videos[4].id
921 const obj = await commands[0].videosExist({ videoIds })
924 const elem = obj[servers[0].videos[0].id]
925 expect(elem).to.have.lengthOf(1)
926 expect(elem[0].playlistElementId).to.exist
927 expect(elem[0].playlistId).to.equal(playlistServer1Id)
928 expect(elem[0].startTimestamp).to.equal(15)
929 expect(elem[0].stopTimestamp).to.equal(28)
933 const elem = obj[servers[0].videos[3].id]
934 expect(elem).to.have.lengthOf(1)
935 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
936 expect(elem[0].playlistId).to.equal(playlistServer1Id)
937 expect(elem[0].startTimestamp).to.equal(1)
938 expect(elem[0].stopTimestamp).to.equal(35)
942 const elem = obj[servers[0].videos[4].id]
943 expect(elem).to.have.lengthOf(1)
944 expect(elem[0].playlistId).to.equal(playlistServer1Id)
945 expect(elem[0].startTimestamp).to.equal(45)
946 expect(elem[0].stopTimestamp).to.equal(null)
949 expect(obj[42000]).to.have.lengthOf(0)
950 expect(obj[43000]).to.have.lengthOf(0)
953 it('Should automatically update updatedAt field of playlists', async function () {
954 const server = servers[1]
955 const videoId = servers[1].videos[5].id
957 async function getPlaylistNames () {
958 const { data } = await server.playlistsCommand.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
960 return data.map(p => p.displayName)
963 const attributes = { videoId }
964 const element1 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id1, attributes })
965 const element2 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id2, attributes })
967 const names1 = await getPlaylistNames()
968 expect(names1[0]).to.equal('playlist 3 updated')
969 expect(names1[1]).to.equal('playlist 2')
971 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
973 const names2 = await getPlaylistNames()
974 expect(names2[0]).to.equal('playlist 2')
975 expect(names2[1]).to.equal('playlist 3 updated')
977 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
979 const names3 = await getPlaylistNames()
980 expect(names3[0]).to.equal('playlist 3 updated')
981 expect(names3[1]).to.equal('playlist 2')
984 it('Should delete some elements', async function () {
987 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
988 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
990 await waitJobs(servers)
992 for (const server of servers) {
993 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
994 expect(body.total).to.equal(6)
996 const elements = body.data
997 expect(elements).to.have.lengthOf(6)
999 expect(elements[0].video.name).to.equal('video 0 server 1')
1000 expect(elements[0].position).to.equal(1)
1002 expect(elements[1].video.name).to.equal('video 2 server 3')
1003 expect(elements[1].position).to.equal(2)
1005 expect(elements[2].video.name).to.equal('video 1 server 3')
1006 expect(elements[2].position).to.equal(3)
1008 expect(elements[3].video.name).to.equal('video 4 server 1')
1009 expect(elements[3].position).to.equal(4)
1011 expect(elements[4].video.name).to.equal('NSFW video')
1012 expect(elements[4].position).to.equal(5)
1014 expect(elements[5].video.name).to.equal('NSFW video')
1015 expect(elements[5].position).to.equal(6)
1019 it('Should be able to create a public playlist, and set it to private', async function () {
1022 const videoPlaylistIds = await commands[0].create({
1024 displayName: 'my super public playlist',
1025 privacy: VideoPlaylistPrivacy.PUBLIC,
1026 videoChannelId: servers[0].videoChannel.id
1030 await waitJobs(servers)
1032 for (const server of servers) {
1033 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1036 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1037 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1039 await waitJobs(servers)
1041 for (const server of [ servers[1], servers[2] ]) {
1042 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1045 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1046 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1050 describe('Playlist deletion', function () {
1052 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1055 await commands[0].delete({ playlistId: playlistServer1Id })
1057 await waitJobs(servers)
1059 for (const server of servers) {
1060 await server.playlistsCommand.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1064 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1067 for (const server of servers) {
1068 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1072 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1075 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1078 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1079 expect(body.total).to.equal(3)
1081 expect(finder(body.data)).to.not.be.undefined
1084 await servers[2].followsCommand.unfollow({ target: servers[0] })
1087 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1088 expect(body.total).to.equal(1)
1090 expect(finder(body.data)).to.be.undefined
1094 it('Should delete a channel and put the associated playlist in private mode', async function () {
1097 const channel = await servers[0].channelsCommand.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1099 const playlistCreated = await commands[0].create({
1101 displayName: 'channel playlist',
1102 privacy: VideoPlaylistPrivacy.PUBLIC,
1103 videoChannelId: channel.id
1107 await waitJobs(servers)
1109 await servers[0].channelsCommand.delete({ channelName: 'super_channel' })
1111 await waitJobs(servers)
1113 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1114 expect(body.displayName).to.equal('channel playlist')
1115 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1117 await servers[1].playlistsCommand.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1120 it('Should delete an account and delete its playlists', async function () {
1123 const user = { username: 'user_1', password: 'password' }
1124 const res = await createUser({
1125 url: servers[0].url,
1126 accessToken: servers[0].accessToken,
1127 username: user.username,
1128 password: user.password
1131 const userId = res.body.user.id
1132 const userAccessToken = await userLogin(servers[0], user)
1134 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1135 const userChannel = (resChannel.body as User).videoChannels[0]
1137 await commands[0].create({
1139 displayName: 'playlist to be deleted',
1140 privacy: VideoPlaylistPrivacy.PUBLIC,
1141 videoChannelId: userChannel.id
1145 await waitJobs(servers)
1147 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1150 for (const server of [ servers[0], servers[1] ]) {
1151 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1153 expect(finder(body.data)).to.not.be.undefined
1157 await removeUser(servers[0].url, userId, servers[0].accessToken)
1158 await waitJobs(servers)
1161 for (const server of [ servers[0], servers[1] ]) {
1162 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1164 expect(finder(body.data)).to.be.undefined
1170 after(async function () {
1171 await cleanupTests(servers)