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