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,
10 flushAndRunMultipleServers,
13 setAccessTokensToServers,
14 setDefaultVideoChannel,
21 } from '@shared/extra-utils'
24 VideoPlaylistCreateResult,
25 VideoPlaylistElementType,
29 } from '@shared/models'
31 const expect = chai.expect
33 async function checkPlaylistElementType (
34 servers: ServerInfo[],
36 type: VideoPlaylistElementType,
41 for (const server of servers) {
42 const body = await server.playlistsCommand.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
43 expect(body.total).to.equal(total)
45 const videoElement = body.data.find(e => e.position === position)
46 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
48 if (type === VideoPlaylistElementType.REGULAR) {
49 expect(videoElement.video).to.not.be.null
50 expect(videoElement.video.name).to.equal(name)
52 expect(videoElement.video).to.be.null
57 describe('Test video playlists', function () {
58 let servers: ServerInfo[] = []
60 let playlistServer2Id1: number
61 let playlistServer2Id2: number
62 let playlistServer2UUID2: string
64 let playlistServer1Id: number
65 let playlistServer1UUID: string
66 let playlistServer1UUID2: string
68 let playlistElementServer1Video4: number
69 let playlistElementServer1Video5: number
70 let playlistElementNSFW: number
72 let nsfwVideoServer1: number
74 let userTokenServer1: string
76 let commands: PlaylistsCommand[]
78 before(async function () {
81 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
83 // Get the access tokens
84 await setAccessTokensToServers(servers)
85 await setDefaultVideoChannel(servers)
87 // Server 1 and server 2 follow each other
88 await doubleFollow(servers[0], servers[1])
89 // Server 1 and server 3 follow each other
90 await doubleFollow(servers[0], servers[2])
92 commands = servers.map(s => s.playlistsCommand)
95 servers[0].videos = []
96 servers[1].videos = []
97 servers[2].videos = []
99 for (const server of servers) {
100 for (let i = 0; i < 7; i++) {
101 const name = `video ${i} server ${server.serverNumber}`
102 const resVideo = await uploadVideo(server.url, server.accessToken, { name, nsfw: false })
104 server.videos.push(resVideo.body.video)
109 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
111 userTokenServer1 = await servers[0].usersCommand.generateUserAndToken('user1')
113 await waitJobs(servers)
116 describe('Get default playlists', function () {
118 it('Should list video playlist privacies', async function () {
119 const privacies = await commands[0].getPrivacies()
121 expect(Object.keys(privacies)).to.have.length.at.least(3)
122 expect(privacies[3]).to.equal('Private')
125 it('Should list watch later playlist', async function () {
126 const token = servers[0].accessToken
129 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
131 expect(body.total).to.equal(1)
132 expect(body.data).to.have.lengthOf(1)
134 const playlist = body.data[0]
135 expect(playlist.displayName).to.equal('Watch later')
136 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
137 expect(playlist.type.label).to.equal('Watch later')
141 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
143 expect(body.total).to.equal(0)
144 expect(body.data).to.have.lengthOf(0)
148 const body = await commands[0].listByAccount({ handle: 'root' })
149 expect(body.total).to.equal(0)
150 expect(body.data).to.have.lengthOf(0)
154 it('Should get private playlist for a classic user', async function () {
155 const token = await servers[0].usersCommand.generateUserAndToken('toto')
157 const body = await commands[0].listByAccount({ token, handle: 'toto' })
159 expect(body.total).to.equal(1)
160 expect(body.data).to.have.lengthOf(1)
162 const playlistId = body.data[0].id
163 await commands[0].listVideos({ token, playlistId })
167 describe('Create and federate playlists', function () {
169 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
172 await commands[0].create({
174 displayName: 'my super playlist',
175 privacy: VideoPlaylistPrivacy.PUBLIC,
176 description: 'my super description',
177 thumbnailfile: 'thumbnail.jpg',
178 videoChannelId: servers[0].videoChannel.id
182 await waitJobs(servers)
183 // Processing a playlist by the receiver could be long
186 for (const server of servers) {
187 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
188 expect(body.total).to.equal(1)
189 expect(body.data).to.have.lengthOf(1)
191 const playlistFromList = body.data[0]
193 const playlistFromGet = await server.playlistsCommand.get({ playlistId: playlistFromList.uuid })
195 for (const playlist of [ playlistFromGet, playlistFromList ]) {
196 expect(playlist.id).to.be.a('number')
197 expect(playlist.uuid).to.be.a('string')
199 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
201 expect(playlist.displayName).to.equal('my super playlist')
202 expect(playlist.description).to.equal('my super description')
203 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
204 expect(playlist.privacy.label).to.equal('Public')
205 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
206 expect(playlist.type.label).to.equal('Regular')
207 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
209 expect(playlist.videosLength).to.equal(0)
211 expect(playlist.ownerAccount.name).to.equal('root')
212 expect(playlist.ownerAccount.displayName).to.equal('root')
213 expect(playlist.videoChannel.name).to.equal('root_channel')
214 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
219 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
223 const playlist = await servers[1].playlistsCommand.create({
225 displayName: 'playlist 2',
226 privacy: VideoPlaylistPrivacy.PUBLIC,
227 videoChannelId: servers[1].videoChannel.id
230 playlistServer2Id1 = playlist.id
234 const playlist = await servers[1].playlistsCommand.create({
236 displayName: 'playlist 3',
237 privacy: VideoPlaylistPrivacy.PUBLIC,
238 thumbnailfile: 'thumbnail.jpg',
239 videoChannelId: servers[1].videoChannel.id
243 playlistServer2Id2 = playlist.id
244 playlistServer2UUID2 = playlist.uuid
247 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
248 await servers[1].playlistsCommand.addElement({
250 attributes: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
252 await servers[1].playlistsCommand.addElement({
254 attributes: { videoId: servers[1].videos[1].id }
258 await waitJobs(servers)
261 for (const server of [ servers[0], servers[1] ]) {
262 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
264 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
265 expect(playlist2).to.not.be.undefined
266 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
268 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
269 expect(playlist3).to.not.be.undefined
270 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
273 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
274 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
275 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
278 it('Should have the playlist on server 3 after a new follow', async function () {
281 // Server 2 and server 3 follow each other
282 await doubleFollow(servers[1], servers[2])
284 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
286 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
287 expect(playlist2).to.not.be.undefined
288 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
290 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
294 describe('List playlists', function () {
296 it('Should correctly list the playlists', async function () {
300 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: 'createdAt' })
301 expect(body.total).to.equal(3)
303 const data = body.data
304 expect(data).to.have.lengthOf(2)
305 expect(data[0].displayName).to.equal('playlist 2')
306 expect(data[1].displayName).to.equal('playlist 3')
310 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: '-createdAt' })
311 expect(body.total).to.equal(3)
313 const data = body.data
314 expect(data).to.have.lengthOf(2)
315 expect(data[0].displayName).to.equal('playlist 2')
316 expect(data[1].displayName).to.equal('my super playlist')
320 it('Should list video channel playlists', async function () {
324 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
325 expect(body.total).to.equal(1)
327 const data = body.data
328 expect(data).to.have.lengthOf(1)
329 expect(data[0].displayName).to.equal('my super playlist')
333 it('Should list account playlists', async function () {
337 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
338 expect(body.total).to.equal(2)
340 const data = body.data
341 expect(data).to.have.lengthOf(1)
342 expect(data[0].displayName).to.equal('playlist 2')
346 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
347 expect(body.total).to.equal(2)
349 const data = body.data
350 expect(data).to.have.lengthOf(1)
351 expect(data[0].displayName).to.equal('playlist 3')
355 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
356 expect(body.total).to.equal(1)
358 const data = body.data
359 expect(data).to.have.lengthOf(1)
360 expect(data[0].displayName).to.equal('playlist 3')
364 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
365 expect(body.total).to.equal(0)
367 const data = body.data
368 expect(data).to.have.lengthOf(0)
373 describe('Playlist rights', function () {
374 let unlistedPlaylist: VideoPlaylistCreateResult
375 let privatePlaylist: VideoPlaylistCreateResult
377 before(async function () {
381 unlistedPlaylist = await servers[1].playlistsCommand.create({
383 displayName: 'playlist unlisted',
384 privacy: VideoPlaylistPrivacy.UNLISTED,
385 videoChannelId: servers[1].videoChannel.id
391 privatePlaylist = await servers[1].playlistsCommand.create({
393 displayName: 'playlist private',
394 privacy: VideoPlaylistPrivacy.PRIVATE
399 await waitJobs(servers)
403 it('Should not list unlisted or private playlists', async function () {
404 for (const server of servers) {
406 await server.playlistsCommand.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
407 await server.playlistsCommand.list({ start: 0, count: 2, sort: '-createdAt' })
410 expect(results[0].total).to.equal(2)
411 expect(results[1].total).to.equal(3)
413 for (const body of results) {
414 const data = body.data
415 expect(data).to.have.lengthOf(2)
416 expect(data[0].displayName).to.equal('playlist 3')
417 expect(data[1].displayName).to.equal('playlist 2')
422 it('Should not get unlisted playlist using only the id', async function () {
423 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
426 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
427 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.uuid })
428 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.shortUUID })
431 it('Should not get private playlist without token', async function () {
432 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
433 await servers[1].playlistsCommand.get({ playlistId: id, expectedStatus: 401 })
437 it('Should get private playlist with a token', async function () {
438 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
439 await servers[1].playlistsCommand.get({ token: servers[1].accessToken, playlistId: id })
444 describe('Update playlists', function () {
446 it('Should update a playlist', async function () {
449 await servers[1].playlistsCommand.update({
451 displayName: 'playlist 3 updated',
452 description: 'description updated',
453 privacy: VideoPlaylistPrivacy.UNLISTED,
454 thumbnailfile: 'thumbnail.jpg',
455 videoChannelId: servers[1].videoChannel.id
457 playlistId: playlistServer2Id2
460 await waitJobs(servers)
462 for (const server of servers) {
463 const playlist = await server.playlistsCommand.get({ playlistId: playlistServer2UUID2 })
465 expect(playlist.displayName).to.equal('playlist 3 updated')
466 expect(playlist.description).to.equal('description updated')
468 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
469 expect(playlist.privacy.label).to.equal('Unlisted')
471 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
472 expect(playlist.type.label).to.equal('Regular')
474 expect(playlist.videosLength).to.equal(2)
476 expect(playlist.ownerAccount.name).to.equal('root')
477 expect(playlist.ownerAccount.displayName).to.equal('root')
478 expect(playlist.videoChannel.name).to.equal('root_channel')
479 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
484 describe('Element timestamps', function () {
486 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
489 const addVideo = (attributes: any) => {
490 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
493 const playlist = await commands[0].create({
495 displayName: 'playlist 4',
496 privacy: VideoPlaylistPrivacy.PUBLIC,
497 videoChannelId: servers[0].videoChannel.id
501 playlistServer1Id = playlist.id
502 playlistServer1UUID = playlist.uuid
504 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
505 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
506 await addVideo({ videoId: servers[2].videos[2].uuid })
508 const element = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
509 playlistElementServer1Video4 = element.id
513 const element = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
514 playlistElementServer1Video5 = element.id
518 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
519 playlistElementNSFW = element.id
521 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
522 await addVideo({ videoId: nsfwVideoServer1 })
525 await waitJobs(servers)
528 it('Should correctly list playlist videos', async function () {
531 for (const server of servers) {
533 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
535 expect(body.total).to.equal(8)
537 const videoElements = body.data
538 expect(videoElements).to.have.lengthOf(8)
540 expect(videoElements[0].video.name).to.equal('video 0 server 1')
541 expect(videoElements[0].position).to.equal(1)
542 expect(videoElements[0].startTimestamp).to.equal(15)
543 expect(videoElements[0].stopTimestamp).to.equal(28)
545 expect(videoElements[1].video.name).to.equal('video 1 server 3')
546 expect(videoElements[1].position).to.equal(2)
547 expect(videoElements[1].startTimestamp).to.equal(35)
548 expect(videoElements[1].stopTimestamp).to.be.null
550 expect(videoElements[2].video.name).to.equal('video 2 server 3')
551 expect(videoElements[2].position).to.equal(3)
552 expect(videoElements[2].startTimestamp).to.be.null
553 expect(videoElements[2].stopTimestamp).to.be.null
555 expect(videoElements[3].video.name).to.equal('video 3 server 1')
556 expect(videoElements[3].position).to.equal(4)
557 expect(videoElements[3].startTimestamp).to.be.null
558 expect(videoElements[3].stopTimestamp).to.equal(35)
560 expect(videoElements[4].video.name).to.equal('video 4 server 1')
561 expect(videoElements[4].position).to.equal(5)
562 expect(videoElements[4].startTimestamp).to.equal(45)
563 expect(videoElements[4].stopTimestamp).to.equal(60)
565 expect(videoElements[5].video.name).to.equal('NSFW video')
566 expect(videoElements[5].position).to.equal(6)
567 expect(videoElements[5].startTimestamp).to.equal(5)
568 expect(videoElements[5].stopTimestamp).to.be.null
570 expect(videoElements[6].video.name).to.equal('NSFW video')
571 expect(videoElements[6].position).to.equal(7)
572 expect(videoElements[6].startTimestamp).to.equal(4)
573 expect(videoElements[6].stopTimestamp).to.be.null
575 expect(videoElements[7].video.name).to.equal('NSFW video')
576 expect(videoElements[7].position).to.equal(8)
577 expect(videoElements[7].startTimestamp).to.be.null
578 expect(videoElements[7].stopTimestamp).to.be.null
582 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
583 expect(body.data).to.have.lengthOf(2)
589 describe('Element type', function () {
590 let groupUser1: ServerInfo[]
591 let groupWithoutToken1: ServerInfo[]
592 let group1: ServerInfo[]
593 let group2: ServerInfo[]
599 before(async function () {
602 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
603 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
604 group1 = [ servers[0] ]
605 group2 = [ servers[1], servers[2] ]
607 const playlist = await commands[0].create({
608 token: userTokenServer1,
610 displayName: 'playlist 56',
611 privacy: VideoPlaylistPrivacy.PUBLIC,
612 videoChannelId: servers[0].videoChannel.id
616 const playlistServer1Id2 = playlist.id
617 playlistServer1UUID2 = playlist.uuid
619 const addVideo = (attributes: any) => {
620 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
623 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userTokenServer1 })).uuid
624 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
625 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
627 await waitJobs(servers)
629 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
630 await addVideo({ videoId: video2, startTimestamp: 35 })
631 await addVideo({ videoId: video3 })
633 await waitJobs(servers)
636 it('Should update the element type if the video is private', async function () {
639 const name = 'video 89'
643 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
644 await waitJobs(servers)
646 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
647 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
648 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
649 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
653 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
654 await waitJobs(servers)
656 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
657 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
658 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
659 // We deleted the video, so even if we recreated it, the old entry is still deleted
660 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
664 it('Should update the element type if the video is blacklisted', async function () {
667 const name = 'video 89'
671 await servers[0].blacklistCommand.add({ videoId: video1, reason: 'reason', unfederate: true })
672 await waitJobs(servers)
674 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
675 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
676 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
677 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
681 await servers[0].blacklistCommand.remove({ videoId: video1 })
682 await waitJobs(servers)
684 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
685 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
686 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
687 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
688 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
692 it('Should update the element type if the account or server of the video is blocked', async function () {
695 const command = servers[0].blocklistCommand
697 const name = 'video 90'
701 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
702 await waitJobs(servers)
704 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
705 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
707 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
708 await waitJobs(servers)
710 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
714 await command.addToMyBlocklist({ token: userTokenServer1, server: '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, server: 'localhost:' + servers[1].port })
721 await waitJobs(servers)
723 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
727 await command.addToServerBlocklist({ account: 'root@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.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
734 await waitJobs(servers)
736 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
740 await command.addToServerBlocklist({ server: '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({ server: 'localhost:' + servers[1].port })
747 await waitJobs(servers)
749 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
753 it('Should hide the video if it is NSFW', async function () {
754 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
755 expect(body.total).to.equal(3)
757 const elements = body.data
758 const element = elements.find(e => e.position === 3)
760 expect(element).to.exist
761 expect(element.video).to.be.null
762 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
767 describe('Managing playlist elements', function () {
769 it('Should reorder the playlist', async function () {
773 await commands[0].reorderElements({
774 playlistId: playlistServer1Id,
777 insertAfterPosition: 3
781 await waitJobs(servers)
783 for (const server of servers) {
784 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
785 const names = body.data.map(v => v.video.name)
787 expect(names).to.deep.equal([
801 await commands[0].reorderElements({
802 playlistId: playlistServer1Id,
806 insertAfterPosition: 4
810 await waitJobs(servers)
812 for (const server of servers) {
813 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
814 const names = body.data.map(v => v.video.name)
816 expect(names).to.deep.equal([
830 await commands[0].reorderElements({
831 playlistId: playlistServer1Id,
834 insertAfterPosition: 3
838 await waitJobs(servers)
840 for (const server of servers) {
841 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
842 const names = elements.map(v => v.video.name)
844 expect(names).to.deep.equal([
855 for (let i = 1; i <= elements.length; i++) {
856 expect(elements[i - 1].position).to.equal(i)
862 it('Should update startTimestamp/endTimestamp of some elements', async function () {
865 await commands[0].updateElement({
866 playlistId: playlistServer1Id,
867 elementId: playlistElementServer1Video4,
873 await commands[0].updateElement({
874 playlistId: playlistServer1Id,
875 elementId: playlistElementServer1Video5,
881 await waitJobs(servers)
883 for (const server of servers) {
884 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
886 expect(elements[0].video.name).to.equal('video 3 server 1')
887 expect(elements[0].position).to.equal(1)
888 expect(elements[0].startTimestamp).to.equal(1)
889 expect(elements[0].stopTimestamp).to.equal(35)
891 expect(elements[5].video.name).to.equal('video 4 server 1')
892 expect(elements[5].position).to.equal(6)
893 expect(elements[5].startTimestamp).to.equal(45)
894 expect(elements[5].stopTimestamp).to.be.null
898 it('Should check videos existence in my playlist', async function () {
900 servers[0].videos[0].id,
902 servers[0].videos[3].id,
904 servers[0].videos[4].id
906 const obj = await commands[0].videosExist({ videoIds })
909 const elem = obj[servers[0].videos[0].id]
910 expect(elem).to.have.lengthOf(1)
911 expect(elem[0].playlistElementId).to.exist
912 expect(elem[0].playlistId).to.equal(playlistServer1Id)
913 expect(elem[0].startTimestamp).to.equal(15)
914 expect(elem[0].stopTimestamp).to.equal(28)
918 const elem = obj[servers[0].videos[3].id]
919 expect(elem).to.have.lengthOf(1)
920 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
921 expect(elem[0].playlistId).to.equal(playlistServer1Id)
922 expect(elem[0].startTimestamp).to.equal(1)
923 expect(elem[0].stopTimestamp).to.equal(35)
927 const elem = obj[servers[0].videos[4].id]
928 expect(elem).to.have.lengthOf(1)
929 expect(elem[0].playlistId).to.equal(playlistServer1Id)
930 expect(elem[0].startTimestamp).to.equal(45)
931 expect(elem[0].stopTimestamp).to.equal(null)
934 expect(obj[42000]).to.have.lengthOf(0)
935 expect(obj[43000]).to.have.lengthOf(0)
938 it('Should automatically update updatedAt field of playlists', async function () {
939 const server = servers[1]
940 const videoId = servers[1].videos[5].id
942 async function getPlaylistNames () {
943 const { data } = await server.playlistsCommand.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
945 return data.map(p => p.displayName)
948 const attributes = { videoId }
949 const element1 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id1, attributes })
950 const element2 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id2, attributes })
952 const names1 = await getPlaylistNames()
953 expect(names1[0]).to.equal('playlist 3 updated')
954 expect(names1[1]).to.equal('playlist 2')
956 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
958 const names2 = await getPlaylistNames()
959 expect(names2[0]).to.equal('playlist 2')
960 expect(names2[1]).to.equal('playlist 3 updated')
962 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
964 const names3 = await getPlaylistNames()
965 expect(names3[0]).to.equal('playlist 3 updated')
966 expect(names3[1]).to.equal('playlist 2')
969 it('Should delete some elements', async function () {
972 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
973 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
975 await waitJobs(servers)
977 for (const server of servers) {
978 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
979 expect(body.total).to.equal(6)
981 const elements = body.data
982 expect(elements).to.have.lengthOf(6)
984 expect(elements[0].video.name).to.equal('video 0 server 1')
985 expect(elements[0].position).to.equal(1)
987 expect(elements[1].video.name).to.equal('video 2 server 3')
988 expect(elements[1].position).to.equal(2)
990 expect(elements[2].video.name).to.equal('video 1 server 3')
991 expect(elements[2].position).to.equal(3)
993 expect(elements[3].video.name).to.equal('video 4 server 1')
994 expect(elements[3].position).to.equal(4)
996 expect(elements[4].video.name).to.equal('NSFW video')
997 expect(elements[4].position).to.equal(5)
999 expect(elements[5].video.name).to.equal('NSFW video')
1000 expect(elements[5].position).to.equal(6)
1004 it('Should be able to create a public playlist, and set it to private', async function () {
1007 const videoPlaylistIds = await commands[0].create({
1009 displayName: 'my super public playlist',
1010 privacy: VideoPlaylistPrivacy.PUBLIC,
1011 videoChannelId: servers[0].videoChannel.id
1015 await waitJobs(servers)
1017 for (const server of servers) {
1018 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1021 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1022 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1024 await waitJobs(servers)
1026 for (const server of [ servers[1], servers[2] ]) {
1027 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1030 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1031 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1035 describe('Playlist deletion', function () {
1037 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1040 await commands[0].delete({ playlistId: playlistServer1Id })
1042 await waitJobs(servers)
1044 for (const server of servers) {
1045 await server.playlistsCommand.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1049 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1052 for (const server of servers) {
1053 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1057 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1060 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1063 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1064 expect(body.total).to.equal(3)
1066 expect(finder(body.data)).to.not.be.undefined
1069 await servers[2].followsCommand.unfollow({ target: servers[0] })
1072 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1073 expect(body.total).to.equal(1)
1075 expect(finder(body.data)).to.be.undefined
1079 it('Should delete a channel and put the associated playlist in private mode', async function () {
1082 const channel = await servers[0].channelsCommand.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1084 const playlistCreated = await commands[0].create({
1086 displayName: 'channel playlist',
1087 privacy: VideoPlaylistPrivacy.PUBLIC,
1088 videoChannelId: channel.id
1092 await waitJobs(servers)
1094 await servers[0].channelsCommand.delete({ channelName: 'super_channel' })
1096 await waitJobs(servers)
1098 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1099 expect(body.displayName).to.equal('channel playlist')
1100 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1102 await servers[1].playlistsCommand.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1105 it('Should delete an account and delete its playlists', async function () {
1108 const { userId, token } = await servers[0].usersCommand.generate('user_1')
1110 const { videoChannels } = await servers[0].usersCommand.getMyInfo({ token })
1111 const userChannel = videoChannels[0]
1113 await commands[0].create({
1115 displayName: 'playlist to be deleted',
1116 privacy: VideoPlaylistPrivacy.PUBLIC,
1117 videoChannelId: userChannel.id
1121 await waitJobs(servers)
1123 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1126 for (const server of [ servers[0], servers[1] ]) {
1127 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1129 expect(finder(body.data)).to.not.be.undefined
1133 await servers[0].usersCommand.remove({ userId })
1134 await waitJobs(servers)
1137 for (const server of [ servers[0], servers[1] ]) {
1138 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1140 expect(finder(body.data)).to.be.undefined
1146 after(async function () {
1147 await cleanupTests(servers)