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