1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
3 import { expect } from 'chai'
4 import { checkPlaylistFilesWereRemoved, testImage } from '@server/tests/shared'
5 import { wait } from '@shared/core-utils'
6 import { uuidToShort } from '@shared/extra-utils'
10 VideoPlaylistCreateResult,
11 VideoPlaylistElementType,
15 } from '@shared/models'
18 createMultipleServers,
22 setAccessTokensToServers,
23 setDefaultAccountAvatar,
24 setDefaultVideoChannel,
26 } from '@shared/server-commands'
28 async function checkPlaylistElementType (
29 servers: PeerTubeServer[],
31 type: VideoPlaylistElementType,
36 for (const server of servers) {
37 const body = await server.playlists.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 })
38 expect(body.total).to.equal(total)
40 const videoElement = body.data.find(e => e.position === position)
41 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
43 if (type === VideoPlaylistElementType.REGULAR) {
44 expect(videoElement.video).to.not.be.null
45 expect(videoElement.video.name).to.equal(name)
47 expect(videoElement.video).to.be.null
52 describe('Test video playlists', function () {
53 let servers: PeerTubeServer[] = []
55 let playlistServer2Id1: number
56 let playlistServer2Id2: number
57 let playlistServer2UUID2: string
59 let playlistServer1Id: number
60 let playlistServer1DisplayName: string
61 let playlistServer1UUID: string
62 let playlistServer1UUID2: string
64 let playlistElementServer1Video4: number
65 let playlistElementServer1Video5: number
66 let playlistElementNSFW: number
68 let nsfwVideoServer1: number
70 let userTokenServer1: string
72 let commands: PlaylistsCommand[]
74 before(async function () {
77 servers = await createMultipleServers(3)
79 // Get the access tokens
80 await setAccessTokensToServers(servers)
81 await setDefaultVideoChannel(servers)
82 await setDefaultAccountAvatar(servers)
84 for (const server of servers) {
85 await server.config.disableTranscoding()
88 // Server 1 and server 2 follow each other
89 await doubleFollow(servers[0], servers[1])
90 // Server 1 and server 3 follow each other
91 await doubleFollow(servers[0], servers[2])
93 commands = servers.map(s => s.playlists)
96 servers[0].store.videos = []
97 servers[1].store.videos = []
98 servers[2].store.videos = []
100 for (const server of servers) {
101 for (let i = 0; i < 7; i++) {
102 const name = `video ${i} server ${server.serverNumber}`
103 const video = await server.videos.upload({ attributes: { name, nsfw: false } })
105 server.store.videos.push(video)
110 nsfwVideoServer1 = (await servers[0].videos.quickUpload({ name: 'NSFW video', nsfw: true })).id
112 userTokenServer1 = await servers[0].users.generateUserAndToken('user1')
114 await waitJobs(servers)
117 describe('Check playlists filters and privacies', function () {
119 it('Should list video playlist privacies', async function () {
120 const privacies = await commands[0].getPrivacies()
122 expect(Object.keys(privacies)).to.have.length.at.least(3)
123 expect(privacies[3]).to.equal('Private')
126 it('Should filter on playlist type', async function () {
129 const token = servers[0].accessToken
131 await commands[0].create({
133 displayName: 'my super playlist',
134 privacy: VideoPlaylistPrivacy.PUBLIC,
135 description: 'my super description',
136 thumbnailfile: 'thumbnail.jpg',
137 videoChannelId: servers[0].store.channel.id
142 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
144 expect(body.total).to.equal(1)
145 expect(body.data).to.have.lengthOf(1)
147 const playlist = body.data[0]
148 expect(playlist.displayName).to.equal('Watch later')
149 expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER)
150 expect(playlist.type.label).to.equal('Watch later')
151 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
155 const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.WATCH_LATER })
156 const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.WATCH_LATER })
158 for (const body of [ bodyList, bodyChannel ]) {
159 expect(body.total).to.equal(0)
160 expect(body.data).to.have.lengthOf(0)
165 const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.REGULAR })
166 const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.REGULAR })
168 let playlist: VideoPlaylist = null
169 for (const body of [ bodyList, bodyChannel ]) {
171 expect(body.total).to.equal(1)
172 expect(body.data).to.have.lengthOf(1)
174 playlist = body.data[0]
175 expect(playlist.displayName).to.equal('my super playlist')
176 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
177 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
180 await commands[0].update({
181 playlistId: playlist.id,
183 privacy: VideoPlaylistPrivacy.PRIVATE
189 const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.REGULAR })
190 const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.REGULAR })
192 for (const body of [ bodyList, bodyChannel ]) {
193 expect(body.total).to.equal(0)
194 expect(body.data).to.have.lengthOf(0)
199 const body = await commands[0].listByAccount({ handle: 'root' })
200 expect(body.total).to.equal(0)
201 expect(body.data).to.have.lengthOf(0)
205 it('Should get private playlist for a classic user', async function () {
206 const token = await servers[0].users.generateUserAndToken('toto')
208 const body = await commands[0].listByAccount({ token, handle: 'toto' })
210 expect(body.total).to.equal(1)
211 expect(body.data).to.have.lengthOf(1)
213 const playlistId = body.data[0].id
214 await commands[0].listVideos({ token, playlistId })
218 describe('Create and federate playlists', function () {
220 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
223 await commands[0].create({
225 displayName: 'my super playlist',
226 privacy: VideoPlaylistPrivacy.PUBLIC,
227 description: 'my super description',
228 thumbnailfile: 'thumbnail.jpg',
229 videoChannelId: servers[0].store.channel.id
233 await waitJobs(servers)
234 // Processing a playlist by the receiver could be long
237 for (const server of servers) {
238 const body = await server.playlists.list({ start: 0, count: 5 })
239 expect(body.total).to.equal(1)
240 expect(body.data).to.have.lengthOf(1)
242 const playlistFromList = body.data[0]
244 const playlistFromGet = await server.playlists.get({ playlistId: playlistFromList.uuid })
246 for (const playlist of [ playlistFromGet, playlistFromList ]) {
247 expect(playlist.id).to.be.a('number')
248 expect(playlist.uuid).to.be.a('string')
250 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
252 expect(playlist.displayName).to.equal('my super playlist')
253 expect(playlist.description).to.equal('my super description')
254 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC)
255 expect(playlist.privacy.label).to.equal('Public')
256 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
257 expect(playlist.type.label).to.equal('Regular')
258 expect(playlist.embedPath).to.equal('/video-playlists/embed/' + playlist.uuid)
260 expect(playlist.videosLength).to.equal(0)
262 expect(playlist.ownerAccount.name).to.equal('root')
263 expect(playlist.ownerAccount.displayName).to.equal('root')
264 expect(playlist.videoChannel.name).to.equal('root_channel')
265 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
270 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
274 const playlist = await servers[1].playlists.create({
276 displayName: 'playlist 2',
277 privacy: VideoPlaylistPrivacy.PUBLIC,
278 videoChannelId: servers[1].store.channel.id
281 playlistServer2Id1 = playlist.id
285 const playlist = await servers[1].playlists.create({
287 displayName: 'playlist 3',
288 privacy: VideoPlaylistPrivacy.PUBLIC,
289 thumbnailfile: 'thumbnail.jpg',
290 videoChannelId: servers[1].store.channel.id
294 playlistServer2Id2 = playlist.id
295 playlistServer2UUID2 = playlist.uuid
298 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
299 await servers[1].playlists.addElement({
301 attributes: { videoId: servers[1].store.videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
303 await servers[1].playlists.addElement({
305 attributes: { videoId: servers[1].store.videos[1].id }
309 await waitJobs(servers)
312 for (const server of [ servers[0], servers[1] ]) {
313 const body = await server.playlists.list({ start: 0, count: 5 })
315 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
316 expect(playlist2).to.not.be.undefined
317 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
319 const playlist3 = body.data.find(p => p.displayName === 'playlist 3')
320 expect(playlist3).to.not.be.undefined
321 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
324 const body = await servers[2].playlists.list({ start: 0, count: 5 })
325 expect(body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
326 expect(body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
329 it('Should have the playlist on server 3 after a new follow', async function () {
332 // Server 2 and server 3 follow each other
333 await doubleFollow(servers[1], servers[2])
335 const body = await servers[2].playlists.list({ start: 0, count: 5 })
337 const playlist2 = body.data.find(p => p.displayName === 'playlist 2')
338 expect(playlist2).to.not.be.undefined
339 await testImage(servers[2].url, 'thumbnail-playlist', playlist2.thumbnailPath)
341 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
345 describe('List playlists', function () {
347 it('Should correctly list the playlists', async function () {
351 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: 'createdAt' })
352 expect(body.total).to.equal(3)
354 const data = body.data
355 expect(data).to.have.lengthOf(2)
356 expect(data[0].displayName).to.equal('playlist 2')
357 expect(data[1].displayName).to.equal('playlist 3')
361 const body = await servers[2].playlists.list({ start: 1, count: 2, sort: '-createdAt' })
362 expect(body.total).to.equal(3)
364 const data = body.data
365 expect(data).to.have.lengthOf(2)
366 expect(data[0].displayName).to.equal('playlist 2')
367 expect(data[1].displayName).to.equal('my super playlist')
371 it('Should list video channel playlists', async function () {
375 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
376 expect(body.total).to.equal(1)
378 const data = body.data
379 expect(data).to.have.lengthOf(1)
380 expect(data[0].displayName).to.equal('my super playlist')
384 it('Should list account playlists', async function () {
388 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
389 expect(body.total).to.equal(2)
391 const data = body.data
392 expect(data).to.have.lengthOf(1)
393 expect(data[0].displayName).to.equal('playlist 2')
397 const body = await servers[1].playlists.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
398 expect(body.total).to.equal(2)
400 const data = body.data
401 expect(data).to.have.lengthOf(1)
402 expect(data[0].displayName).to.equal('playlist 3')
406 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
407 expect(body.total).to.equal(1)
409 const data = body.data
410 expect(data).to.have.lengthOf(1)
411 expect(data[0].displayName).to.equal('playlist 3')
415 const body = await servers[1].playlists.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
416 expect(body.total).to.equal(0)
418 const data = body.data
419 expect(data).to.have.lengthOf(0)
424 describe('Playlist rights', function () {
425 let unlistedPlaylist: VideoPlaylistCreateResult
426 let privatePlaylist: VideoPlaylistCreateResult
428 before(async function () {
432 unlistedPlaylist = await servers[1].playlists.create({
434 displayName: 'playlist unlisted',
435 privacy: VideoPlaylistPrivacy.UNLISTED,
436 videoChannelId: servers[1].store.channel.id
442 privatePlaylist = await servers[1].playlists.create({
444 displayName: 'playlist private',
445 privacy: VideoPlaylistPrivacy.PRIVATE
450 await waitJobs(servers)
454 it('Should not list unlisted or private playlists', async function () {
455 for (const server of servers) {
457 await server.playlists.listByAccount({ handle: 'root@' + servers[1].host, sort: '-createdAt' }),
458 await server.playlists.list({ start: 0, count: 2, sort: '-createdAt' })
461 expect(results[0].total).to.equal(2)
462 expect(results[1].total).to.equal(3)
464 for (const body of results) {
465 const data = body.data
466 expect(data).to.have.lengthOf(2)
467 expect(data[0].displayName).to.equal('playlist 3')
468 expect(data[1].displayName).to.equal('playlist 2')
473 it('Should not get unlisted playlist using only the id', async function () {
474 await servers[1].playlists.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
477 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
478 await servers[1].playlists.get({ playlistId: unlistedPlaylist.uuid })
479 await servers[1].playlists.get({ playlistId: unlistedPlaylist.shortUUID })
482 it('Should not get private playlist without token', async function () {
483 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
484 await servers[1].playlists.get({ playlistId: id, expectedStatus: 401 })
488 it('Should get private playlist with a token', async function () {
489 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
490 await servers[1].playlists.get({ token: servers[1].accessToken, playlistId: id })
495 describe('Update playlists', function () {
497 it('Should update a playlist', async function () {
500 await servers[1].playlists.update({
502 displayName: 'playlist 3 updated',
503 description: 'description updated',
504 privacy: VideoPlaylistPrivacy.UNLISTED,
505 thumbnailfile: 'thumbnail.jpg',
506 videoChannelId: servers[1].store.channel.id
508 playlistId: playlistServer2Id2
511 await waitJobs(servers)
513 for (const server of servers) {
514 const playlist = await server.playlists.get({ playlistId: playlistServer2UUID2 })
516 expect(playlist.displayName).to.equal('playlist 3 updated')
517 expect(playlist.description).to.equal('description updated')
519 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
520 expect(playlist.privacy.label).to.equal('Unlisted')
522 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
523 expect(playlist.type.label).to.equal('Regular')
525 expect(playlist.videosLength).to.equal(2)
527 expect(playlist.ownerAccount.name).to.equal('root')
528 expect(playlist.ownerAccount.displayName).to.equal('root')
529 expect(playlist.videoChannel.name).to.equal('root_channel')
530 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
535 describe('Element timestamps', function () {
537 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
540 const addVideo = (attributes: any) => {
541 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
544 const playlistDisplayName = 'playlist 4'
545 const playlist = await commands[0].create({
547 displayName: playlistDisplayName,
548 privacy: VideoPlaylistPrivacy.PUBLIC,
549 videoChannelId: servers[0].store.channel.id
553 playlistServer1Id = playlist.id
554 playlistServer1DisplayName = playlistDisplayName
555 playlistServer1UUID = playlist.uuid
557 await addVideo({ videoId: servers[0].store.videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
558 await addVideo({ videoId: servers[2].store.videos[1].uuid, startTimestamp: 35 })
559 await addVideo({ videoId: servers[2].store.videos[2].uuid })
561 const element = await addVideo({ videoId: servers[0].store.videos[3].uuid, stopTimestamp: 35 })
562 playlistElementServer1Video4 = element.id
566 const element = await addVideo({ videoId: servers[0].store.videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
567 playlistElementServer1Video5 = element.id
571 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
572 playlistElementNSFW = element.id
574 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
575 await addVideo({ videoId: nsfwVideoServer1 })
578 await waitJobs(servers)
581 it('Should correctly list playlist videos', async function () {
584 for (const server of servers) {
586 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
588 expect(body.total).to.equal(8)
590 const videoElements = body.data
591 expect(videoElements).to.have.lengthOf(8)
593 expect(videoElements[0].video.name).to.equal('video 0 server 1')
594 expect(videoElements[0].position).to.equal(1)
595 expect(videoElements[0].startTimestamp).to.equal(15)
596 expect(videoElements[0].stopTimestamp).to.equal(28)
598 expect(videoElements[1].video.name).to.equal('video 1 server 3')
599 expect(videoElements[1].position).to.equal(2)
600 expect(videoElements[1].startTimestamp).to.equal(35)
601 expect(videoElements[1].stopTimestamp).to.be.null
603 expect(videoElements[2].video.name).to.equal('video 2 server 3')
604 expect(videoElements[2].position).to.equal(3)
605 expect(videoElements[2].startTimestamp).to.be.null
606 expect(videoElements[2].stopTimestamp).to.be.null
608 expect(videoElements[3].video.name).to.equal('video 3 server 1')
609 expect(videoElements[3].position).to.equal(4)
610 expect(videoElements[3].startTimestamp).to.be.null
611 expect(videoElements[3].stopTimestamp).to.equal(35)
613 expect(videoElements[4].video.name).to.equal('video 4 server 1')
614 expect(videoElements[4].position).to.equal(5)
615 expect(videoElements[4].startTimestamp).to.equal(45)
616 expect(videoElements[4].stopTimestamp).to.equal(60)
618 expect(videoElements[5].video.name).to.equal('NSFW video')
619 expect(videoElements[5].position).to.equal(6)
620 expect(videoElements[5].startTimestamp).to.equal(5)
621 expect(videoElements[5].stopTimestamp).to.be.null
623 expect(videoElements[6].video.name).to.equal('NSFW video')
624 expect(videoElements[6].position).to.equal(7)
625 expect(videoElements[6].startTimestamp).to.equal(4)
626 expect(videoElements[6].stopTimestamp).to.be.null
628 expect(videoElements[7].video.name).to.equal('NSFW video')
629 expect(videoElements[7].position).to.equal(8)
630 expect(videoElements[7].startTimestamp).to.be.null
631 expect(videoElements[7].stopTimestamp).to.be.null
635 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
636 expect(body.data).to.have.lengthOf(2)
642 describe('Element type', function () {
643 let groupUser1: PeerTubeServer[]
644 let groupWithoutToken1: PeerTubeServer[]
645 let group1: PeerTubeServer[]
646 let group2: PeerTubeServer[]
652 before(async function () {
655 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userTokenServer1 }) ]
656 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
657 group1 = [ servers[0] ]
658 group2 = [ servers[1], servers[2] ]
660 const playlist = await commands[0].create({
661 token: userTokenServer1,
663 displayName: 'playlist 56',
664 privacy: VideoPlaylistPrivacy.PUBLIC,
665 videoChannelId: servers[0].store.channel.id
669 const playlistServer1Id2 = playlist.id
670 playlistServer1UUID2 = playlist.uuid
672 const addVideo = (attributes: any) => {
673 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
676 video1 = (await servers[0].videos.quickUpload({ name: 'video 89', token: userTokenServer1 })).uuid
677 video2 = (await servers[1].videos.quickUpload({ name: 'video 90' })).uuid
678 video3 = (await servers[0].videos.quickUpload({ name: 'video 91', nsfw: true })).uuid
680 await waitJobs(servers)
682 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
683 await addVideo({ videoId: video2, startTimestamp: 35 })
684 await addVideo({ videoId: video3 })
686 await waitJobs(servers)
689 it('Should update the element type if the video is private', async function () {
692 const name = 'video 89'
696 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PRIVATE } })
697 await waitJobs(servers)
699 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
700 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
701 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
702 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
706 await servers[0].videos.update({ id: video1, attributes: { privacy: VideoPrivacy.PUBLIC } })
707 await waitJobs(servers)
709 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
710 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
711 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
712 // We deleted the video, so even if we recreated it, the old entry is still deleted
713 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
717 it('Should update the element type if the video is blacklisted', async function () {
720 const name = 'video 89'
724 await servers[0].blacklist.add({ videoId: video1, reason: 'reason', unfederate: true })
725 await waitJobs(servers)
727 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
728 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
729 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
730 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
734 await servers[0].blacklist.remove({ videoId: video1 })
735 await waitJobs(servers)
737 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
738 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
739 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
740 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
741 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
745 it('Should update the element type if the account or server of the video is blocked', async function () {
748 const command = servers[0].blocklist
750 const name = 'video 90'
754 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@' + servers[1].host })
755 await waitJobs(servers)
757 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
758 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
760 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@' + servers[1].host })
761 await waitJobs(servers)
763 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
767 await command.addToMyBlocklist({ token: userTokenServer1, server: servers[1].host })
768 await waitJobs(servers)
770 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
771 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
773 await command.removeFromMyBlocklist({ token: userTokenServer1, server: servers[1].host })
774 await waitJobs(servers)
776 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
780 await command.addToServerBlocklist({ account: 'root@' + servers[1].host })
781 await waitJobs(servers)
783 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
784 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
786 await command.removeFromServerBlocklist({ account: 'root@' + servers[1].host })
787 await waitJobs(servers)
789 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
793 await command.addToServerBlocklist({ server: servers[1].host })
794 await waitJobs(servers)
796 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
797 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
799 await command.removeFromServerBlocklist({ server: servers[1].host })
800 await waitJobs(servers)
802 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
807 describe('Managing playlist elements', function () {
809 it('Should reorder the playlist', async function () {
813 await commands[0].reorderElements({
814 playlistId: playlistServer1Id,
817 insertAfterPosition: 3
821 await waitJobs(servers)
823 for (const server of servers) {
824 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
825 const names = body.data.map(v => v.video.name)
827 expect(names).to.deep.equal([
841 await commands[0].reorderElements({
842 playlistId: playlistServer1Id,
846 insertAfterPosition: 4
850 await waitJobs(servers)
852 for (const server of servers) {
853 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
854 const names = body.data.map(v => v.video.name)
856 expect(names).to.deep.equal([
870 await commands[0].reorderElements({
871 playlistId: playlistServer1Id,
874 insertAfterPosition: 3
878 await waitJobs(servers)
880 for (const server of servers) {
881 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
882 const names = elements.map(v => v.video.name)
884 expect(names).to.deep.equal([
895 for (let i = 1; i <= elements.length; i++) {
896 expect(elements[i - 1].position).to.equal(i)
902 it('Should update startTimestamp/endTimestamp of some elements', async function () {
905 await commands[0].updateElement({
906 playlistId: playlistServer1Id,
907 elementId: playlistElementServer1Video4,
913 await commands[0].updateElement({
914 playlistId: playlistServer1Id,
915 elementId: playlistElementServer1Video5,
921 await waitJobs(servers)
923 for (const server of servers) {
924 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
926 expect(elements[0].video.name).to.equal('video 3 server 1')
927 expect(elements[0].position).to.equal(1)
928 expect(elements[0].startTimestamp).to.equal(1)
929 expect(elements[0].stopTimestamp).to.equal(35)
931 expect(elements[5].video.name).to.equal('video 4 server 1')
932 expect(elements[5].position).to.equal(6)
933 expect(elements[5].startTimestamp).to.equal(45)
934 expect(elements[5].stopTimestamp).to.be.null
938 it('Should check videos existence in my playlist', async function () {
940 servers[0].store.videos[0].id,
942 servers[0].store.videos[3].id,
944 servers[0].store.videos[4].id
946 const obj = await commands[0].videosExist({ videoIds })
949 const elem = obj[servers[0].store.videos[0].id]
950 expect(elem).to.have.lengthOf(1)
951 expect(elem[0].playlistElementId).to.exist
952 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
953 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
954 expect(elem[0].playlistId).to.equal(playlistServer1Id)
955 expect(elem[0].startTimestamp).to.equal(15)
956 expect(elem[0].stopTimestamp).to.equal(28)
960 const elem = obj[servers[0].store.videos[3].id]
961 expect(elem).to.have.lengthOf(1)
962 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
963 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
964 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
965 expect(elem[0].playlistId).to.equal(playlistServer1Id)
966 expect(elem[0].startTimestamp).to.equal(1)
967 expect(elem[0].stopTimestamp).to.equal(35)
971 const elem = obj[servers[0].store.videos[4].id]
972 expect(elem).to.have.lengthOf(1)
973 expect(elem[0].playlistId).to.equal(playlistServer1Id)
974 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
975 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
976 expect(elem[0].startTimestamp).to.equal(45)
977 expect(elem[0].stopTimestamp).to.equal(null)
980 expect(obj[42000]).to.have.lengthOf(0)
981 expect(obj[43000]).to.have.lengthOf(0)
984 it('Should automatically update updatedAt field of playlists', async function () {
985 const server = servers[1]
986 const videoId = servers[1].store.videos[5].id
988 async function getPlaylistNames () {
989 const { data } = await server.playlists.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
991 return data.map(p => p.displayName)
994 const attributes = { videoId }
995 const element1 = await server.playlists.addElement({ playlistId: playlistServer2Id1, attributes })
996 const element2 = await server.playlists.addElement({ playlistId: playlistServer2Id2, attributes })
998 const names1 = await getPlaylistNames()
999 expect(names1[0]).to.equal('playlist 3 updated')
1000 expect(names1[1]).to.equal('playlist 2')
1002 await server.playlists.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
1004 const names2 = await getPlaylistNames()
1005 expect(names2[0]).to.equal('playlist 2')
1006 expect(names2[1]).to.equal('playlist 3 updated')
1008 await server.playlists.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
1010 const names3 = await getPlaylistNames()
1011 expect(names3[0]).to.equal('playlist 3 updated')
1012 expect(names3[1]).to.equal('playlist 2')
1015 it('Should delete some elements', async function () {
1018 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
1019 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
1021 await waitJobs(servers)
1023 for (const server of servers) {
1024 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
1025 expect(body.total).to.equal(6)
1027 const elements = body.data
1028 expect(elements).to.have.lengthOf(6)
1030 expect(elements[0].video.name).to.equal('video 0 server 1')
1031 expect(elements[0].position).to.equal(1)
1033 expect(elements[1].video.name).to.equal('video 2 server 3')
1034 expect(elements[1].position).to.equal(2)
1036 expect(elements[2].video.name).to.equal('video 1 server 3')
1037 expect(elements[2].position).to.equal(3)
1039 expect(elements[3].video.name).to.equal('video 4 server 1')
1040 expect(elements[3].position).to.equal(4)
1042 expect(elements[4].video.name).to.equal('NSFW video')
1043 expect(elements[4].position).to.equal(5)
1045 expect(elements[5].video.name).to.equal('NSFW video')
1046 expect(elements[5].position).to.equal(6)
1050 it('Should be able to create a public playlist, and set it to private', async function () {
1053 const videoPlaylistIds = await commands[0].create({
1055 displayName: 'my super public playlist',
1056 privacy: VideoPlaylistPrivacy.PUBLIC,
1057 videoChannelId: servers[0].store.channel.id
1061 await waitJobs(servers)
1063 for (const server of servers) {
1064 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1067 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1068 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1070 await waitJobs(servers)
1072 for (const server of [ servers[1], servers[2] ]) {
1073 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1076 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1077 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1081 describe('Playlist deletion', function () {
1083 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1086 await commands[0].delete({ playlistId: playlistServer1Id })
1088 await waitJobs(servers)
1090 for (const server of servers) {
1091 await server.playlists.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1095 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1098 for (const server of servers) {
1099 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server)
1103 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1106 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1109 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1110 expect(body.total).to.equal(3)
1112 expect(finder(body.data)).to.not.be.undefined
1115 await servers[2].follows.unfollow({ target: servers[0] })
1118 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1119 expect(body.total).to.equal(1)
1121 expect(finder(body.data)).to.be.undefined
1125 it('Should delete a channel and put the associated playlist in private mode', async function () {
1128 const channel = await servers[0].channels.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1130 const playlistCreated = await commands[0].create({
1132 displayName: 'channel playlist',
1133 privacy: VideoPlaylistPrivacy.PUBLIC,
1134 videoChannelId: channel.id
1138 await waitJobs(servers)
1140 await servers[0].channels.delete({ channelName: 'super_channel' })
1142 await waitJobs(servers)
1144 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1145 expect(body.displayName).to.equal('channel playlist')
1146 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1148 await servers[1].playlists.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1151 it('Should delete an account and delete its playlists', async function () {
1154 const { userId, token } = await servers[0].users.generate('user_1')
1156 const { videoChannels } = await servers[0].users.getMyInfo({ token })
1157 const userChannel = videoChannels[0]
1159 await commands[0].create({
1161 displayName: 'playlist to be deleted',
1162 privacy: VideoPlaylistPrivacy.PUBLIC,
1163 videoChannelId: userChannel.id
1167 await waitJobs(servers)
1169 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1172 for (const server of [ servers[0], servers[1] ]) {
1173 const body = await server.playlists.list({ start: 0, count: 15 })
1175 expect(finder(body.data)).to.not.be.undefined
1179 await servers[0].users.remove({ userId })
1180 await waitJobs(servers)
1183 for (const server of [ servers[0], servers[1] ]) {
1184 const body = await server.playlists.list({ start: 0, count: 15 })
1186 expect(finder(body.data)).to.be.undefined
1192 after(async function () {
1193 await cleanupTests(servers)