]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/video-playlists.ts
2bb019348929a9645f2b1139d1f7b764df7b16c9
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / video-playlists.ts
1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3 import 'mocha'
4 import * as chai from 'chai'
5 import { HttpStatusCode } from '@shared/core-utils'
6 import {
7 checkPlaylistFilesWereRemoved,
8 cleanupTests,
9 createUser,
10 doubleFollow,
11 flushAndRunMultipleServers,
12 generateUserAccessToken,
13 getAccessToken,
14 getMyUserInformation,
15 PlaylistsCommand,
16 removeUser,
17 ServerInfo,
18 setAccessTokensToServers,
19 setDefaultVideoChannel,
20 testImage,
21 updateVideo,
22 uploadVideo,
23 uploadVideoAndGetId,
24 userLogin,
25 wait,
26 waitJobs
27 } from '@shared/extra-utils'
28 import {
29 User,
30 VideoPlaylist,
31 VideoPlaylistCreateResult,
32 VideoPlaylistElementType,
33 VideoPlaylistPrivacy,
34 VideoPlaylistType,
35 VideoPrivacy
36 } from '@shared/models'
37
38 const expect = chai.expect
39
40 async function checkPlaylistElementType (
41 servers: ServerInfo[],
42 playlistId: string,
43 type: VideoPlaylistElementType,
44 position: number,
45 name: string,
46 total: number
47 ) {
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)
51
52 const videoElement = body.data.find(e => e.position === position)
53 expect(videoElement.type).to.equal(type, 'On server ' + server.url)
54
55 if (type === VideoPlaylistElementType.REGULAR) {
56 expect(videoElement.video).to.not.be.null
57 expect(videoElement.video.name).to.equal(name)
58 } else {
59 expect(videoElement.video).to.be.null
60 }
61 }
62 }
63
64 describe('Test video playlists', function () {
65 let servers: ServerInfo[] = []
66
67 let playlistServer2Id1: number
68 let playlistServer2Id2: number
69 let playlistServer2UUID2: string
70
71 let playlistServer1Id: number
72 let playlistServer1UUID: string
73 let playlistServer1UUID2: string
74
75 let playlistElementServer1Video4: number
76 let playlistElementServer1Video5: number
77 let playlistElementNSFW: number
78
79 let nsfwVideoServer1: number
80
81 let userTokenServer1: string
82
83 let commands: PlaylistsCommand[]
84
85 before(async function () {
86 this.timeout(120000)
87
88 servers = await flushAndRunMultipleServers(3, { transcoding: { enabled: false } })
89
90 // Get the access tokens
91 await setAccessTokensToServers(servers)
92 await setDefaultVideoChannel(servers)
93
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])
98
99 commands = servers.map(s => s.playlistsCommand)
100
101 {
102 servers[0].videos = []
103 servers[1].videos = []
104 servers[2].videos = []
105
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 })
110
111 server.videos.push(resVideo.body.video)
112 }
113 }
114 }
115
116 nsfwVideoServer1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'NSFW video', nsfw: true })).id
117
118 {
119 await createUser({
120 url: servers[0].url,
121 accessToken: servers[0].accessToken,
122 username: 'user1',
123 password: 'password'
124 })
125 userTokenServer1 = await getAccessToken(servers[0].url, 'user1', 'password')
126 }
127
128 await waitJobs(servers)
129 })
130
131 describe('Get default playlists', function () {
132
133 it('Should list video playlist privacies', async function () {
134 const privacies = await commands[0].getPrivacies()
135
136 expect(Object.keys(privacies)).to.have.length.at.least(3)
137 expect(privacies[3]).to.equal('Private')
138 })
139
140 it('Should list watch later playlist', async function () {
141 const token = servers[0].accessToken
142
143 {
144 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER })
145
146 expect(body.total).to.equal(1)
147 expect(body.data).to.have.lengthOf(1)
148
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')
153 }
154
155 {
156 const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR })
157
158 expect(body.total).to.equal(0)
159 expect(body.data).to.have.lengthOf(0)
160 }
161
162 {
163 const body = await commands[0].listByAccount({ handle: 'root' })
164 expect(body.total).to.equal(0)
165 expect(body.data).to.have.lengthOf(0)
166 }
167 })
168
169 it('Should get private playlist for a classic user', async function () {
170 const token = await generateUserAccessToken(servers[0], 'toto')
171
172 const body = await commands[0].listByAccount({ token, handle: 'toto' })
173
174 expect(body.total).to.equal(1)
175 expect(body.data).to.have.lengthOf(1)
176
177 const playlistId = body.data[0].id
178 await commands[0].listVideos({ token, playlistId })
179 })
180 })
181
182 describe('Create and federate playlists', function () {
183
184 it('Should create a playlist on server 1 and have the playlist on server 2 and 3', async function () {
185 this.timeout(30000)
186
187 await commands[0].create({
188 attributes: {
189 displayName: 'my super playlist',
190 privacy: VideoPlaylistPrivacy.PUBLIC,
191 description: 'my super description',
192 thumbnailfile: 'thumbnail.jpg',
193 videoChannelId: servers[0].videoChannel.id
194 }
195 })
196
197 await waitJobs(servers)
198 // Processing a playlist by the receiver could be long
199 await wait(3000)
200
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)
205
206 const playlistFromList = body.data[0]
207
208 const playlistFromGet = await server.playlistsCommand.get({ playlistId: playlistFromList.uuid })
209
210 for (const playlist of [ playlistFromGet, playlistFromList ]) {
211 expect(playlist.id).to.be.a('number')
212 expect(playlist.uuid).to.be.a('string')
213
214 expect(playlist.isLocal).to.equal(server.serverNumber === 1)
215
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)
223
224 expect(playlist.videosLength).to.equal(0)
225
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')
230 }
231 }
232 })
233
234 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
235 this.timeout(30000)
236
237 {
238 const playlist = await servers[1].playlistsCommand.create({
239 attributes: {
240 displayName: 'playlist 2',
241 privacy: VideoPlaylistPrivacy.PUBLIC,
242 videoChannelId: servers[1].videoChannel.id
243 }
244 })
245 playlistServer2Id1 = playlist.id
246 }
247
248 {
249 const playlist = await servers[1].playlistsCommand.create({
250 attributes: {
251 displayName: 'playlist 3',
252 privacy: VideoPlaylistPrivacy.PUBLIC,
253 thumbnailfile: 'thumbnail.jpg',
254 videoChannelId: servers[1].videoChannel.id
255 }
256 })
257
258 playlistServer2Id2 = playlist.id
259 playlistServer2UUID2 = playlist.uuid
260 }
261
262 for (const id of [ playlistServer2Id1, playlistServer2Id2 ]) {
263 await servers[1].playlistsCommand.addElement({
264 playlistId: id,
265 attributes: { videoId: servers[1].videos[0].id, startTimestamp: 1, stopTimestamp: 2 }
266 })
267 await servers[1].playlistsCommand.addElement({
268 playlistId: id,
269 attributes: { videoId: servers[1].videos[1].id }
270 })
271 }
272
273 await waitJobs(servers)
274 await wait(3000)
275
276 for (const server of [ servers[0], servers[1] ]) {
277 const body = await server.playlistsCommand.list({ start: 0, count: 5 })
278
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)
282
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)
286 }
287
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
291 })
292
293 it('Should have the playlist on server 3 after a new follow', async function () {
294 this.timeout(30000)
295
296 // Server 2 and server 3 follow each other
297 await doubleFollow(servers[1], servers[2])
298
299 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
300
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)
304
305 expect(body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
306 })
307 })
308
309 describe('List playlists', function () {
310
311 it('Should correctly list the playlists', async function () {
312 this.timeout(30000)
313
314 {
315 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: 'createdAt' })
316 expect(body.total).to.equal(3)
317
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')
322 }
323
324 {
325 const body = await servers[2].playlistsCommand.list({ start: 1, count: 2, sort: '-createdAt' })
326 expect(body.total).to.equal(3)
327
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')
332 }
333 })
334
335 it('Should list video channel playlists', async function () {
336 this.timeout(30000)
337
338 {
339 const body = await commands[0].listByChannel({ handle: 'root_channel', start: 0, count: 2, sort: '-createdAt' })
340 expect(body.total).to.equal(1)
341
342 const data = body.data
343 expect(data).to.have.lengthOf(1)
344 expect(data[0].displayName).to.equal('my super playlist')
345 }
346 })
347
348 it('Should list account playlists', async function () {
349 this.timeout(30000)
350
351 {
352 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: '-createdAt' })
353 expect(body.total).to.equal(2)
354
355 const data = body.data
356 expect(data).to.have.lengthOf(1)
357 expect(data[0].displayName).to.equal('playlist 2')
358 }
359
360 {
361 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', start: 1, count: 2, sort: 'createdAt' })
362 expect(body.total).to.equal(2)
363
364 const data = body.data
365 expect(data).to.have.lengthOf(1)
366 expect(data[0].displayName).to.equal('playlist 3')
367 }
368
369 {
370 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '3' })
371 expect(body.total).to.equal(1)
372
373 const data = body.data
374 expect(data).to.have.lengthOf(1)
375 expect(data[0].displayName).to.equal('playlist 3')
376 }
377
378 {
379 const body = await servers[1].playlistsCommand.listByAccount({ handle: 'root', sort: 'createdAt', search: '4' })
380 expect(body.total).to.equal(0)
381
382 const data = body.data
383 expect(data).to.have.lengthOf(0)
384 }
385 })
386 })
387
388 describe('Playlist rights', function () {
389 let unlistedPlaylist: VideoPlaylistCreateResult
390 let privatePlaylist: VideoPlaylistCreateResult
391
392 before(async function () {
393 this.timeout(30000)
394
395 {
396 unlistedPlaylist = await servers[1].playlistsCommand.create({
397 attributes: {
398 displayName: 'playlist unlisted',
399 privacy: VideoPlaylistPrivacy.UNLISTED,
400 videoChannelId: servers[1].videoChannel.id
401 }
402 })
403 }
404
405 {
406 privatePlaylist = await servers[1].playlistsCommand.create({
407 attributes: {
408 displayName: 'playlist private',
409 privacy: VideoPlaylistPrivacy.PRIVATE
410 }
411 })
412 }
413
414 await waitJobs(servers)
415 await wait(3000)
416 })
417
418 it('Should not list unlisted or private playlists', async function () {
419 for (const server of servers) {
420 const results = [
421 await server.playlistsCommand.listByAccount({ handle: 'root@localhost:' + servers[1].port, sort: '-createdAt' }),
422 await server.playlistsCommand.list({ start: 0, count: 2, sort: '-createdAt' })
423 ]
424
425 expect(results[0].total).to.equal(2)
426 expect(results[1].total).to.equal(3)
427
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')
433 }
434 }
435 })
436
437 it('Should not get unlisted playlist using only the id', async function () {
438 await servers[1].playlistsCommand.get({ playlistId: unlistedPlaylist.id, expectedStatus: 404 })
439 })
440
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 })
444 })
445
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 })
449 }
450 })
451
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 })
455 }
456 })
457 })
458
459 describe('Update playlists', function () {
460
461 it('Should update a playlist', async function () {
462 this.timeout(30000)
463
464 await servers[1].playlistsCommand.update({
465 attributes: {
466 displayName: 'playlist 3 updated',
467 description: 'description updated',
468 privacy: VideoPlaylistPrivacy.UNLISTED,
469 thumbnailfile: 'thumbnail.jpg',
470 videoChannelId: servers[1].videoChannel.id
471 },
472 playlistId: playlistServer2Id2
473 })
474
475 await waitJobs(servers)
476
477 for (const server of servers) {
478 const playlist = await server.playlistsCommand.get({ playlistId: playlistServer2UUID2 })
479
480 expect(playlist.displayName).to.equal('playlist 3 updated')
481 expect(playlist.description).to.equal('description updated')
482
483 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
484 expect(playlist.privacy.label).to.equal('Unlisted')
485
486 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
487 expect(playlist.type.label).to.equal('Regular')
488
489 expect(playlist.videosLength).to.equal(2)
490
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')
495 }
496 })
497 })
498
499 describe('Element timestamps', function () {
500
501 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
502 this.timeout(30000)
503
504 const addVideo = (attributes: any) => {
505 return commands[0].addElement({ playlistId: playlistServer1Id, attributes })
506 }
507
508 const playlist = await commands[0].create({
509 attributes: {
510 displayName: 'playlist 4',
511 privacy: VideoPlaylistPrivacy.PUBLIC,
512 videoChannelId: servers[0].videoChannel.id
513 }
514 })
515
516 playlistServer1Id = playlist.id
517 playlistServer1UUID = playlist.uuid
518
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 })
522 {
523 const element = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
524 playlistElementServer1Video4 = element.id
525 }
526
527 {
528 const element = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
529 playlistElementServer1Video5 = element.id
530 }
531
532 {
533 const element = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
534 playlistElementNSFW = element.id
535
536 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
537 await addVideo({ videoId: nsfwVideoServer1 })
538 }
539
540 await waitJobs(servers)
541 })
542
543 it('Should correctly list playlist videos', async function () {
544 this.timeout(30000)
545
546 for (const server of servers) {
547 {
548 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
549
550 expect(body.total).to.equal(8)
551
552 const videoElements = body.data
553 expect(videoElements).to.have.lengthOf(8)
554
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)
559
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
564
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
569
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)
574
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)
579
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
584
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
589
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
594 }
595
596 {
597 const body = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 2 })
598 expect(body.data).to.have.lengthOf(2)
599 }
600 }
601 })
602 })
603
604 describe('Element type', function () {
605 let groupUser1: ServerInfo[]
606 let groupWithoutToken1: ServerInfo[]
607 let group1: ServerInfo[]
608 let group2: ServerInfo[]
609
610 let video1: string
611 let video2: string
612 let video3: string
613
614 before(async function () {
615 this.timeout(60000)
616
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] ]
621
622 const playlist = await commands[0].create({
623 token: userTokenServer1,
624 attributes: {
625 displayName: 'playlist 56',
626 privacy: VideoPlaylistPrivacy.PUBLIC,
627 videoChannelId: servers[0].videoChannel.id
628 }
629 })
630
631 const playlistServer1Id2 = playlist.id
632 playlistServer1UUID2 = playlist.uuid
633
634 const addVideo = (attributes: any) => {
635 return commands[0].addElement({ token: userTokenServer1, playlistId: playlistServer1Id2, attributes })
636 }
637
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
641
642 await waitJobs(servers)
643
644 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
645 await addVideo({ videoId: video2, startTimestamp: 35 })
646 await addVideo({ videoId: video3 })
647
648 await waitJobs(servers)
649 })
650
651 it('Should update the element type if the video is private', async function () {
652 this.timeout(20000)
653
654 const name = 'video 89'
655 const position = 1
656
657 {
658 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
659 await waitJobs(servers)
660
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)
665 }
666
667 {
668 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
669 await waitJobs(servers)
670
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)
676 }
677 })
678
679 it('Should update the element type if the video is blacklisted', async function () {
680 this.timeout(20000)
681
682 const name = 'video 89'
683 const position = 1
684
685 {
686 await servers[0].blacklistCommand.add({ videoId: video1, reason: 'reason', unfederate: true })
687 await waitJobs(servers)
688
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)
693 }
694
695 {
696 await servers[0].blacklistCommand.remove({ videoId: video1 })
697 await waitJobs(servers)
698
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)
704 }
705 })
706
707 it('Should update the element type if the account or server of the video is blocked', async function () {
708 this.timeout(90000)
709
710 const command = servers[0].blocklistCommand
711
712 const name = 'video 90'
713 const position = 2
714
715 {
716 await command.addToMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
717 await waitJobs(servers)
718
719 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
720 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
721
722 await command.removeFromMyBlocklist({ token: userTokenServer1, account: 'root@localhost:' + servers[1].port })
723 await waitJobs(servers)
724
725 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
726 }
727
728 {
729 await command.addToMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
730 await waitJobs(servers)
731
732 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
733 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
734
735 await command.removeFromMyBlocklist({ token: userTokenServer1, server: 'localhost:' + servers[1].port })
736 await waitJobs(servers)
737
738 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
739 }
740
741 {
742 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
743 await waitJobs(servers)
744
745 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
746 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
747
748 await command.removeFromServerBlocklist({ account: 'root@localhost:' + servers[1].port })
749 await waitJobs(servers)
750
751 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
752 }
753
754 {
755 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
756 await waitJobs(servers)
757
758 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
759 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
760
761 await command.removeFromServerBlocklist({ server: 'localhost:' + servers[1].port })
762 await waitJobs(servers)
763
764 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
765 }
766 })
767
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)
771
772 const elements = body.data
773 const element = elements.find(e => e.position === 3)
774
775 expect(element).to.exist
776 expect(element.video).to.be.null
777 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
778 })
779
780 })
781
782 describe('Managing playlist elements', function () {
783
784 it('Should reorder the playlist', async function () {
785 this.timeout(30000)
786
787 {
788 await commands[0].reorderElements({
789 playlistId: playlistServer1Id,
790 attributes: {
791 startPosition: 2,
792 insertAfterPosition: 3
793 }
794 })
795
796 await waitJobs(servers)
797
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)
801
802 expect(names).to.deep.equal([
803 'video 0 server 1',
804 'video 2 server 3',
805 'video 1 server 3',
806 'video 3 server 1',
807 'video 4 server 1',
808 'NSFW video',
809 'NSFW video',
810 'NSFW video'
811 ])
812 }
813 }
814
815 {
816 await commands[0].reorderElements({
817 playlistId: playlistServer1Id,
818 attributes: {
819 startPosition: 1,
820 reorderLength: 3,
821 insertAfterPosition: 4
822 }
823 })
824
825 await waitJobs(servers)
826
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)
830
831 expect(names).to.deep.equal([
832 'video 3 server 1',
833 'video 0 server 1',
834 'video 2 server 3',
835 'video 1 server 3',
836 'video 4 server 1',
837 'NSFW video',
838 'NSFW video',
839 'NSFW video'
840 ])
841 }
842 }
843
844 {
845 await commands[0].reorderElements({
846 playlistId: playlistServer1Id,
847 attributes: {
848 startPosition: 6,
849 insertAfterPosition: 3
850 }
851 })
852
853 await waitJobs(servers)
854
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)
858
859 expect(names).to.deep.equal([
860 'video 3 server 1',
861 'video 0 server 1',
862 'video 2 server 3',
863 'NSFW video',
864 'video 1 server 3',
865 'video 4 server 1',
866 'NSFW video',
867 'NSFW video'
868 ])
869
870 for (let i = 1; i <= elements.length; i++) {
871 expect(elements[i - 1].position).to.equal(i)
872 }
873 }
874 }
875 })
876
877 it('Should update startTimestamp/endTimestamp of some elements', async function () {
878 this.timeout(30000)
879
880 await commands[0].updateElement({
881 playlistId: playlistServer1Id,
882 elementId: playlistElementServer1Video4,
883 attributes: {
884 startTimestamp: 1
885 }
886 })
887
888 await commands[0].updateElement({
889 playlistId: playlistServer1Id,
890 elementId: playlistElementServer1Video5,
891 attributes: {
892 stopTimestamp: null
893 }
894 })
895
896 await waitJobs(servers)
897
898 for (const server of servers) {
899 const { data: elements } = await server.playlistsCommand.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
900
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)
905
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
910 }
911 })
912
913 it('Should check videos existence in my playlist', async function () {
914 const videoIds = [
915 servers[0].videos[0].id,
916 42000,
917 servers[0].videos[3].id,
918 43000,
919 servers[0].videos[4].id
920 ]
921 const obj = await commands[0].videosExist({ videoIds })
922
923 {
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)
930 }
931
932 {
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)
939 }
940
941 {
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)
947 }
948
949 expect(obj[42000]).to.have.lengthOf(0)
950 expect(obj[43000]).to.have.lengthOf(0)
951 })
952
953 it('Should automatically update updatedAt field of playlists', async function () {
954 const server = servers[1]
955 const videoId = servers[1].videos[5].id
956
957 async function getPlaylistNames () {
958 const { data } = await server.playlistsCommand.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
959
960 return data.map(p => p.displayName)
961 }
962
963 const attributes = { videoId }
964 const element1 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id1, attributes })
965 const element2 = await server.playlistsCommand.addElement({ playlistId: playlistServer2Id2, attributes })
966
967 const names1 = await getPlaylistNames()
968 expect(names1[0]).to.equal('playlist 3 updated')
969 expect(names1[1]).to.equal('playlist 2')
970
971 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
972
973 const names2 = await getPlaylistNames()
974 expect(names2[0]).to.equal('playlist 2')
975 expect(names2[1]).to.equal('playlist 3 updated')
976
977 await server.playlistsCommand.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
978
979 const names3 = await getPlaylistNames()
980 expect(names3[0]).to.equal('playlist 3 updated')
981 expect(names3[1]).to.equal('playlist 2')
982 })
983
984 it('Should delete some elements', async function () {
985 this.timeout(30000)
986
987 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
988 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
989
990 await waitJobs(servers)
991
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)
995
996 const elements = body.data
997 expect(elements).to.have.lengthOf(6)
998
999 expect(elements[0].video.name).to.equal('video 0 server 1')
1000 expect(elements[0].position).to.equal(1)
1001
1002 expect(elements[1].video.name).to.equal('video 2 server 3')
1003 expect(elements[1].position).to.equal(2)
1004
1005 expect(elements[2].video.name).to.equal('video 1 server 3')
1006 expect(elements[2].position).to.equal(3)
1007
1008 expect(elements[3].video.name).to.equal('video 4 server 1')
1009 expect(elements[3].position).to.equal(4)
1010
1011 expect(elements[4].video.name).to.equal('NSFW video')
1012 expect(elements[4].position).to.equal(5)
1013
1014 expect(elements[5].video.name).to.equal('NSFW video')
1015 expect(elements[5].position).to.equal(6)
1016 }
1017 })
1018
1019 it('Should be able to create a public playlist, and set it to private', async function () {
1020 this.timeout(30000)
1021
1022 const videoPlaylistIds = await commands[0].create({
1023 attributes: {
1024 displayName: 'my super public playlist',
1025 privacy: VideoPlaylistPrivacy.PUBLIC,
1026 videoChannelId: servers[0].videoChannel.id
1027 }
1028 })
1029
1030 await waitJobs(servers)
1031
1032 for (const server of servers) {
1033 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1034 }
1035
1036 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1037 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1038
1039 await waitJobs(servers)
1040
1041 for (const server of [ servers[1], servers[2] ]) {
1042 await server.playlistsCommand.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1043 }
1044
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 })
1047 })
1048 })
1049
1050 describe('Playlist deletion', function () {
1051
1052 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1053 this.timeout(30000)
1054
1055 await commands[0].delete({ playlistId: playlistServer1Id })
1056
1057 await waitJobs(servers)
1058
1059 for (const server of servers) {
1060 await server.playlistsCommand.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1061 }
1062 })
1063
1064 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1065 this.timeout(30000)
1066
1067 for (const server of servers) {
1068 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1069 }
1070 })
1071
1072 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1073 this.timeout(30000)
1074
1075 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1076
1077 {
1078 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1079 expect(body.total).to.equal(3)
1080
1081 expect(finder(body.data)).to.not.be.undefined
1082 }
1083
1084 await servers[2].followsCommand.unfollow({ target: servers[0] })
1085
1086 {
1087 const body = await servers[2].playlistsCommand.list({ start: 0, count: 5 })
1088 expect(body.total).to.equal(1)
1089
1090 expect(finder(body.data)).to.be.undefined
1091 }
1092 })
1093
1094 it('Should delete a channel and put the associated playlist in private mode', async function () {
1095 this.timeout(30000)
1096
1097 const channel = await servers[0].channelsCommand.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1098
1099 const playlistCreated = await commands[0].create({
1100 attributes: {
1101 displayName: 'channel playlist',
1102 privacy: VideoPlaylistPrivacy.PUBLIC,
1103 videoChannelId: channel.id
1104 }
1105 })
1106
1107 await waitJobs(servers)
1108
1109 await servers[0].channelsCommand.delete({ channelName: 'super_channel' })
1110
1111 await waitJobs(servers)
1112
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)
1116
1117 await servers[1].playlistsCommand.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1118 })
1119
1120 it('Should delete an account and delete its playlists', async function () {
1121 this.timeout(30000)
1122
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
1129 })
1130
1131 const userId = res.body.user.id
1132 const userAccessToken = await userLogin(servers[0], user)
1133
1134 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1135 const userChannel = (resChannel.body as User).videoChannels[0]
1136
1137 await commands[0].create({
1138 attributes: {
1139 displayName: 'playlist to be deleted',
1140 privacy: VideoPlaylistPrivacy.PUBLIC,
1141 videoChannelId: userChannel.id
1142 }
1143 })
1144
1145 await waitJobs(servers)
1146
1147 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1148
1149 {
1150 for (const server of [ servers[0], servers[1] ]) {
1151 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1152
1153 expect(finder(body.data)).to.not.be.undefined
1154 }
1155 }
1156
1157 await removeUser(servers[0].url, userId, servers[0].accessToken)
1158 await waitJobs(servers)
1159
1160 {
1161 for (const server of [ servers[0], servers[1] ]) {
1162 const body = await server.playlistsCommand.list({ start: 0, count: 15 })
1163
1164 expect(finder(body.data)).to.be.undefined
1165 }
1166 }
1167 })
1168 })
1169
1170 after(async function () {
1171 await cleanupTests(servers)
1172 })
1173 })