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