]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/video-playlists.ts
Fix playlist search
[github/Chocobozzz/PeerTube.git] / server / tests / api / videos / video-playlists.ts
1 /* tslint:disable:no-unused-expression */
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
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
254 expect(playlist.videosLength).to.equal(0)
255
256 expect(playlist.ownerAccount.name).to.equal('root')
257 expect(playlist.ownerAccount.displayName).to.equal('root')
258 expect(playlist.videoChannel.name).to.equal('root_channel')
259 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
260 }
261 }
262 })
263
264 it('Should create a playlist on server 2 and have the playlist on server 1 but not on server 3', async function () {
265 this.timeout(30000)
266
267 {
268 const res = await createVideoPlaylist({
269 url: servers[ 1 ].url,
270 token: servers[ 1 ].accessToken,
271 playlistAttrs: {
272 displayName: 'playlist 2',
273 privacy: VideoPlaylistPrivacy.PUBLIC,
274 videoChannelId: servers[ 1 ].videoChannel.id
275 }
276 })
277 playlistServer2Id1 = res.body.videoPlaylist.id
278 }
279
280 {
281 const res = await createVideoPlaylist({
282 url: servers[ 1 ].url,
283 token: servers[ 1 ].accessToken,
284 playlistAttrs: {
285 displayName: 'playlist 3',
286 privacy: VideoPlaylistPrivacy.PUBLIC,
287 thumbnailfile: 'thumbnail.jpg',
288 videoChannelId: servers[ 1 ].videoChannel.id
289 }
290 })
291
292 playlistServer2Id2 = res.body.videoPlaylist.id
293 playlistServer2UUID2 = res.body.videoPlaylist.uuid
294 }
295
296 for (let id of [ playlistServer2Id1, playlistServer2Id2 ]) {
297 await addVideoInPlaylist({
298 url: servers[ 1 ].url,
299 token: servers[ 1 ].accessToken,
300 playlistId: id,
301 elementAttrs: { videoId: servers[ 1 ].videos[ 0 ].id, startTimestamp: 1, stopTimestamp: 2 }
302 })
303 await addVideoInPlaylist({
304 url: servers[ 1 ].url,
305 token: servers[ 1 ].accessToken,
306 playlistId: id,
307 elementAttrs: { videoId: servers[ 1 ].videos[ 1 ].id }
308 })
309 }
310
311 await waitJobs(servers)
312
313 for (const server of [ servers[ 0 ], servers[ 1 ] ]) {
314 const res = await getVideoPlaylistsList(server.url, 0, 5)
315
316 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
317 expect(playlist2).to.not.be.undefined
318 await testImage(server.url, 'thumbnail-playlist', playlist2.thumbnailPath)
319
320 const playlist3 = res.body.data.find(p => p.displayName === 'playlist 3')
321 expect(playlist3).to.not.be.undefined
322 await testImage(server.url, 'thumbnail', playlist3.thumbnailPath)
323 }
324
325 const res = await getVideoPlaylistsList(servers[ 2 ].url, 0, 5)
326 expect(res.body.data.find(p => p.displayName === 'playlist 2')).to.be.undefined
327 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.be.undefined
328 })
329
330 it('Should have the playlist on server 3 after a new follow', async function () {
331 this.timeout(30000)
332
333 // Server 2 and server 3 follow each other
334 await doubleFollow(servers[ 1 ], servers[ 2 ])
335
336 const res = await getVideoPlaylistsList(servers[ 2 ].url, 0, 5)
337
338 const playlist2 = res.body.data.find(p => p.displayName === 'playlist 2')
339 expect(playlist2).to.not.be.undefined
340 await testImage(servers[ 2 ].url, 'thumbnail-playlist', playlist2.thumbnailPath)
341
342 expect(res.body.data.find(p => p.displayName === 'playlist 3')).to.not.be.undefined
343 })
344 })
345
346 describe('List playlists', function () {
347
348 it('Should correctly list the playlists', async function () {
349 this.timeout(30000)
350
351 {
352 const res = await getVideoPlaylistsList(servers[ 2 ].url, 1, 2, 'createdAt')
353
354 expect(res.body.total).to.equal(3)
355
356 const data: VideoPlaylist[] = res.body.data
357 expect(data).to.have.lengthOf(2)
358 expect(data[ 0 ].displayName).to.equal('playlist 2')
359 expect(data[ 1 ].displayName).to.equal('playlist 3')
360 }
361
362 {
363 const res = await getVideoPlaylistsList(servers[ 2 ].url, 1, 2, '-createdAt')
364
365 expect(res.body.total).to.equal(3)
366
367 const data: VideoPlaylist[] = res.body.data
368 expect(data).to.have.lengthOf(2)
369 expect(data[ 0 ].displayName).to.equal('playlist 2')
370 expect(data[ 1 ].displayName).to.equal('my super playlist')
371 }
372 })
373
374 it('Should list video channel playlists', async function () {
375 this.timeout(30000)
376
377 {
378 const res = await getVideoChannelPlaylistsList(servers[ 0 ].url, 'root_channel', 0, 2, '-createdAt')
379
380 expect(res.body.total).to.equal(1)
381
382 const data: VideoPlaylist[] = res.body.data
383 expect(data).to.have.lengthOf(1)
384 expect(data[ 0 ].displayName).to.equal('my super playlist')
385 }
386 })
387
388 it('Should list account playlists', async function () {
389 this.timeout(30000)
390
391 {
392 const res = await getAccountPlaylistsList(servers[ 1 ].url, 'root', 1, 2, '-createdAt')
393
394 expect(res.body.total).to.equal(2)
395
396 const data: VideoPlaylist[] = res.body.data
397 expect(data).to.have.lengthOf(1)
398 expect(data[ 0 ].displayName).to.equal('playlist 2')
399 }
400
401 {
402 const res = await getAccountPlaylistsList(servers[ 1 ].url, 'root', 1, 2, 'createdAt')
403
404 expect(res.body.total).to.equal(2)
405
406 const data: VideoPlaylist[] = res.body.data
407 expect(data).to.have.lengthOf(1)
408 expect(data[ 0 ].displayName).to.equal('playlist 3')
409 }
410
411 {
412 const res = await getAccountPlaylistsList(servers[ 1 ].url, 'root', 0, 10, 'createdAt', '3')
413
414 expect(res.body.total).to.equal(1)
415
416 const data: VideoPlaylist[] = res.body.data
417 expect(data).to.have.lengthOf(1)
418 expect(data[ 0 ].displayName).to.equal('playlist 3')
419 }
420
421 {
422 const res = await getAccountPlaylistsList(servers[ 1 ].url, 'root', 0, 10, 'createdAt', '4')
423
424 expect(res.body.total).to.equal(0)
425
426 const data: VideoPlaylist[] = res.body.data
427 expect(data).to.have.lengthOf(0)
428 }
429 })
430
431 it('Should not list unlisted or private playlists', async function () {
432 this.timeout(30000)
433
434 await createVideoPlaylist({
435 url: servers[ 1 ].url,
436 token: servers[ 1 ].accessToken,
437 playlistAttrs: {
438 displayName: 'playlist unlisted',
439 privacy: VideoPlaylistPrivacy.UNLISTED
440 }
441 })
442
443 await createVideoPlaylist({
444 url: servers[ 1 ].url,
445 token: servers[ 1 ].accessToken,
446 playlistAttrs: {
447 displayName: 'playlist private',
448 privacy: VideoPlaylistPrivacy.PRIVATE
449 }
450 })
451
452 await waitJobs(servers)
453
454 for (const server of servers) {
455 const results = [
456 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[ 1 ].port, 0, 5, '-createdAt'),
457 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
458 ]
459
460 expect(results[ 0 ].body.total).to.equal(2)
461 expect(results[ 1 ].body.total).to.equal(3)
462
463 for (const res of results) {
464 const data: VideoPlaylist[] = res.body.data
465 expect(data).to.have.lengthOf(2)
466 expect(data[ 0 ].displayName).to.equal('playlist 3')
467 expect(data[ 1 ].displayName).to.equal('playlist 2')
468 }
469 }
470 })
471 })
472
473 describe('Update playlists', function () {
474
475 it('Should update a playlist', async function () {
476 this.timeout(30000)
477
478 await updateVideoPlaylist({
479 url: servers[1].url,
480 token: servers[1].accessToken,
481 playlistAttrs: {
482 displayName: 'playlist 3 updated',
483 description: 'description updated',
484 privacy: VideoPlaylistPrivacy.UNLISTED,
485 thumbnailfile: 'thumbnail.jpg',
486 videoChannelId: servers[1].videoChannel.id
487 },
488 playlistId: playlistServer2Id2
489 })
490
491 await waitJobs(servers)
492
493 for (const server of servers) {
494 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
495 const playlist: VideoPlaylist = res.body
496
497 expect(playlist.displayName).to.equal('playlist 3 updated')
498 expect(playlist.description).to.equal('description updated')
499
500 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
501 expect(playlist.privacy.label).to.equal('Unlisted')
502
503 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
504 expect(playlist.type.label).to.equal('Regular')
505
506 expect(playlist.videosLength).to.equal(2)
507
508 expect(playlist.ownerAccount.name).to.equal('root')
509 expect(playlist.ownerAccount.displayName).to.equal('root')
510 expect(playlist.videoChannel.name).to.equal('root_channel')
511 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
512 }
513 })
514 })
515
516 describe('Element timestamps', function () {
517
518 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
519 this.timeout(30000)
520
521 const addVideo = (elementAttrs: any) => {
522 return addVideoInPlaylist({ url: servers[ 0 ].url, token: servers[ 0 ].accessToken, playlistId: playlistServer1Id, elementAttrs })
523 }
524
525 const res = await createVideoPlaylist({
526 url: servers[ 0 ].url,
527 token: servers[ 0 ].accessToken,
528 playlistAttrs: {
529 displayName: 'playlist 4',
530 privacy: VideoPlaylistPrivacy.PUBLIC,
531 videoChannelId: servers[ 0 ].videoChannel.id
532 }
533 })
534
535 playlistServer1Id = res.body.videoPlaylist.id
536 playlistServer1UUID = res.body.videoPlaylist.uuid
537
538 await addVideo({ videoId: servers[ 0 ].videos[ 0 ].uuid, startTimestamp: 15, stopTimestamp: 28 })
539 await addVideo({ videoId: servers[ 2 ].videos[ 1 ].uuid, startTimestamp: 35 })
540 await addVideo({ videoId: servers[ 2 ].videos[ 2 ].uuid })
541 {
542 const res = await addVideo({ videoId: servers[ 0 ].videos[ 3 ].uuid, stopTimestamp: 35 })
543 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
544 }
545
546 {
547 const res = await addVideo({ videoId: servers[ 0 ].videos[ 4 ].uuid, startTimestamp: 45, stopTimestamp: 60 })
548 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
549 }
550
551 {
552 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
553 playlistElementNSFW = res.body.videoPlaylistElement.id
554 }
555
556 await waitJobs(servers)
557 })
558
559 it('Should correctly list playlist videos', async function () {
560 this.timeout(30000)
561
562 for (const server of servers) {
563 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
564
565 expect(res.body.total).to.equal(6)
566
567 const videoElements: VideoPlaylistElement[] = res.body.data
568 expect(videoElements).to.have.lengthOf(6)
569
570 expect(videoElements[ 0 ].video.name).to.equal('video 0 server 1')
571 expect(videoElements[ 0 ].position).to.equal(1)
572 expect(videoElements[ 0 ].startTimestamp).to.equal(15)
573 expect(videoElements[ 0 ].stopTimestamp).to.equal(28)
574
575 expect(videoElements[ 1 ].video.name).to.equal('video 1 server 3')
576 expect(videoElements[ 1 ].position).to.equal(2)
577 expect(videoElements[ 1 ].startTimestamp).to.equal(35)
578 expect(videoElements[ 1 ].stopTimestamp).to.be.null
579
580 expect(videoElements[ 2 ].video.name).to.equal('video 2 server 3')
581 expect(videoElements[ 2 ].position).to.equal(3)
582 expect(videoElements[ 2 ].startTimestamp).to.be.null
583 expect(videoElements[ 2 ].stopTimestamp).to.be.null
584
585 expect(videoElements[ 3 ].video.name).to.equal('video 3 server 1')
586 expect(videoElements[ 3 ].position).to.equal(4)
587 expect(videoElements[ 3 ].startTimestamp).to.be.null
588 expect(videoElements[ 3 ].stopTimestamp).to.equal(35)
589
590 expect(videoElements[ 4 ].video.name).to.equal('video 4 server 1')
591 expect(videoElements[ 4 ].position).to.equal(5)
592 expect(videoElements[ 4 ].startTimestamp).to.equal(45)
593 expect(videoElements[ 4 ].stopTimestamp).to.equal(60)
594
595 expect(videoElements[ 5 ].video.name).to.equal('NSFW video')
596 expect(videoElements[ 5 ].position).to.equal(6)
597 expect(videoElements[ 5 ].startTimestamp).to.equal(5)
598 expect(videoElements[ 5 ].stopTimestamp).to.be.null
599
600 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
601 expect(res3.body.data).to.have.lengthOf(2)
602 }
603 })
604 })
605
606 describe('Element type', function () {
607 let groupUser1: ServerInfo[]
608 let groupWithoutToken1: ServerInfo[]
609 let group1: ServerInfo[]
610 let group2: ServerInfo[]
611
612 let video1: string
613 let video2: string
614 let video3: string
615
616 before(async function () {
617 this.timeout(30000)
618
619 groupUser1 = [ Object.assign({}, servers[ 0 ], { accessToken: userAccessTokenServer1 }) ]
620 groupWithoutToken1 = [ Object.assign({}, servers[ 0 ], { accessToken: undefined }) ]
621 group1 = [ servers[ 0 ] ]
622 group2 = [ servers[ 1 ], servers[ 2 ] ]
623
624 const res = await createVideoPlaylist({
625 url: servers[ 0 ].url,
626 token: userAccessTokenServer1,
627 playlistAttrs: {
628 displayName: 'playlist 56',
629 privacy: VideoPlaylistPrivacy.PUBLIC,
630 videoChannelId: servers[ 0 ].videoChannel.id
631 }
632 })
633
634 const playlistServer1Id2 = res.body.videoPlaylist.id
635 playlistServer1UUID2 = res.body.videoPlaylist.uuid
636
637 const addVideo = (elementAttrs: any) => {
638 return addVideoInPlaylist({ url: servers[ 0 ].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
639 }
640
641 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
642 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
643 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
644
645 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
646 await addVideo({ videoId: video2, startTimestamp: 35 })
647 await addVideo({ videoId: video3 })
648
649 await waitJobs(servers)
650 })
651
652 it('Should update the element type if the video is private', async function () {
653 this.timeout(20000)
654
655 const name = 'video 89'
656 const position = 1
657
658 {
659 await updateVideo(servers[ 0 ].url, servers[ 0 ].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
660 await waitJobs(servers)
661
662 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
663 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
664 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
665 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
666 }
667
668 {
669 await updateVideo(servers[ 0 ].url, servers[ 0 ].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
670 await waitJobs(servers)
671
672 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
673 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
674 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
675 // We deleted the video, so even if we recreated it, the old entry is still deleted
676 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
677 }
678 })
679
680 it('Should update the element type if the video is blacklisted', async function () {
681 this.timeout(20000)
682
683 const name = 'video 89'
684 const position = 1
685
686 {
687 await addVideoToBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video1, 'reason', true)
688 await waitJobs(servers)
689
690 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
691 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
692 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
693 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
694 }
695
696 {
697 await removeVideoFromBlacklist(servers[ 0 ].url, servers[ 0 ].accessToken, video1)
698 await waitJobs(servers)
699
700 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
701 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
702 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
703 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
704 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
705 }
706 })
707
708 it('Should update the element type if the account or server of the video is blocked', async function () {
709 this.timeout(90000)
710
711 const name = 'video 90'
712 const position = 2
713
714 {
715 await addAccountToAccountBlocklist(servers[ 0 ].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
716 await waitJobs(servers)
717
718 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
719 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
720
721 await removeAccountFromAccountBlocklist(servers[ 0 ].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
722 await waitJobs(servers)
723
724 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
725 }
726
727 {
728 await addServerToAccountBlocklist(servers[ 0 ].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
729 await waitJobs(servers)
730
731 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
732 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
733
734 await removeServerFromAccountBlocklist(servers[ 0 ].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
735 await waitJobs(servers)
736
737 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
738 }
739
740 {
741 await addAccountToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'root@localhost:' + servers[1].port)
742 await waitJobs(servers)
743
744 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
745 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
746
747 await removeAccountFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'root@localhost:' + servers[1].port)
748 await waitJobs(servers)
749
750 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
751 }
752
753 {
754 await addServerToServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port)
755 await waitJobs(servers)
756
757 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
758 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
759
760 await removeServerFromServerBlocklist(servers[ 0 ].url, servers[ 0 ].accessToken, 'localhost:' + servers[1].port)
761 await waitJobs(servers)
762
763 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
764 }
765 })
766
767 it('Should hide the video if it is NSFW', async function () {
768 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
769 expect(res.body.total).to.equal(3)
770
771 const elements: VideoPlaylistElement[] = res.body.data
772 const element = elements.find(e => e.position === 3)
773
774 expect(element).to.exist
775 expect(element.video).to.be.null
776 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
777 })
778
779 })
780
781 describe('Managing playlist elements', function () {
782
783 it('Should reorder the playlist', async function () {
784 this.timeout(30000)
785
786 {
787 await reorderVideosPlaylist({
788 url: servers[ 0 ].url,
789 token: servers[ 0 ].accessToken,
790 playlistId: playlistServer1Id,
791 elementAttrs: {
792 startPosition: 2,
793 insertAfterPosition: 3
794 }
795 })
796
797 await waitJobs(servers)
798
799 for (const server of servers) {
800 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
801 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
802
803 expect(names).to.deep.equal([
804 'video 0 server 1',
805 'video 2 server 3',
806 'video 1 server 3',
807 'video 3 server 1',
808 'video 4 server 1',
809 'NSFW video'
810 ])
811 }
812 }
813
814 {
815 await reorderVideosPlaylist({
816 url: servers[ 0 ].url,
817 token: servers[ 0 ].accessToken,
818 playlistId: playlistServer1Id,
819 elementAttrs: {
820 startPosition: 1,
821 reorderLength: 3,
822 insertAfterPosition: 4
823 }
824 })
825
826 await waitJobs(servers)
827
828 for (const server of servers) {
829 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
830 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
831
832 expect(names).to.deep.equal([
833 'video 3 server 1',
834 'video 0 server 1',
835 'video 2 server 3',
836 'video 1 server 3',
837 'video 4 server 1',
838 'NSFW video'
839 ])
840 }
841 }
842
843 {
844 await reorderVideosPlaylist({
845 url: servers[ 0 ].url,
846 token: servers[ 0 ].accessToken,
847 playlistId: playlistServer1Id,
848 elementAttrs: {
849 startPosition: 6,
850 insertAfterPosition: 3
851 }
852 })
853
854 await waitJobs(servers)
855
856 for (const server of servers) {
857 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
858 const elements: VideoPlaylistElement[] = res.body.data
859 const names = elements.map(v => v.video.name)
860
861 expect(names).to.deep.equal([
862 'video 3 server 1',
863 'video 0 server 1',
864 'video 2 server 3',
865 'NSFW video',
866 'video 1 server 3',
867 'video 4 server 1'
868 ])
869
870 for (let i = 1; i <= elements.length; i++) {
871 expect(elements[ i - 1 ].position).to.equal(i)
872 }
873 }
874 }
875 })
876
877 it('Should update startTimestamp/endTimestamp of some elements', async function () {
878 this.timeout(30000)
879
880 await updateVideoPlaylistElement({
881 url: servers[ 0 ].url,
882 token: servers[ 0 ].accessToken,
883 playlistId: playlistServer1Id,
884 playlistElementId: playlistElementServer1Video4,
885 elementAttrs: {
886 startTimestamp: 1
887 }
888 })
889
890 await updateVideoPlaylistElement({
891 url: servers[ 0 ].url,
892 token: servers[ 0 ].accessToken,
893 playlistId: playlistServer1Id,
894 playlistElementId: playlistElementServer1Video5,
895 elementAttrs: {
896 stopTimestamp: null
897 }
898 })
899
900 await waitJobs(servers)
901
902 for (const server of servers) {
903 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
904 const elements: VideoPlaylistElement[] = res.body.data
905
906 expect(elements[ 0 ].video.name).to.equal('video 3 server 1')
907 expect(elements[ 0 ].position).to.equal(1)
908 expect(elements[ 0 ].startTimestamp).to.equal(1)
909 expect(elements[ 0 ].stopTimestamp).to.equal(35)
910
911 expect(elements[ 5 ].video.name).to.equal('video 4 server 1')
912 expect(elements[ 5 ].position).to.equal(6)
913 expect(elements[ 5 ].startTimestamp).to.equal(45)
914 expect(elements[ 5 ].stopTimestamp).to.be.null
915 }
916 })
917
918 it('Should check videos existence in my playlist', async function () {
919 const videoIds = [
920 servers[ 0 ].videos[ 0 ].id,
921 42000,
922 servers[ 0 ].videos[ 3 ].id,
923 43000,
924 servers[ 0 ].videos[ 4 ].id
925 ]
926 const res = await doVideosExistInMyPlaylist(servers[ 0 ].url, servers[ 0 ].accessToken, videoIds)
927 const obj = res.body as VideoExistInPlaylist
928
929 {
930 const elem = obj[ servers[ 0 ].videos[ 0 ].id ]
931 expect(elem).to.have.lengthOf(1)
932 expect(elem[ 0 ].playlistElementId).to.exist
933 expect(elem[ 0 ].playlistId).to.equal(playlistServer1Id)
934 expect(elem[ 0 ].startTimestamp).to.equal(15)
935 expect(elem[ 0 ].stopTimestamp).to.equal(28)
936 }
937
938 {
939 const elem = obj[ servers[ 0 ].videos[ 3 ].id ]
940 expect(elem).to.have.lengthOf(1)
941 expect(elem[ 0 ].playlistElementId).to.equal(playlistElementServer1Video4)
942 expect(elem[ 0 ].playlistId).to.equal(playlistServer1Id)
943 expect(elem[ 0 ].startTimestamp).to.equal(1)
944 expect(elem[ 0 ].stopTimestamp).to.equal(35)
945 }
946
947 {
948 const elem = obj[ servers[ 0 ].videos[ 4 ].id ]
949 expect(elem).to.have.lengthOf(1)
950 expect(elem[ 0 ].playlistId).to.equal(playlistServer1Id)
951 expect(elem[ 0 ].startTimestamp).to.equal(45)
952 expect(elem[ 0 ].stopTimestamp).to.equal(null)
953 }
954
955 expect(obj[ 42000 ]).to.have.lengthOf(0)
956 expect(obj[ 43000 ]).to.have.lengthOf(0)
957 })
958
959 it('Should automatically update updatedAt field of playlists', async function () {
960 const server = servers[ 1 ]
961 const videoId = servers[ 1 ].videos[ 5 ].id
962
963 async function getPlaylistNames () {
964 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
965
966 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
967 }
968
969 const elementAttrs = { videoId }
970 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
971 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
972
973 const element1 = res1.body.videoPlaylistElement.id
974 const element2 = res2.body.videoPlaylistElement.id
975
976 const names1 = await getPlaylistNames()
977 expect(names1[ 0 ]).to.equal('playlist 3 updated')
978 expect(names1[ 1 ]).to.equal('playlist 2')
979
980 await removeVideoFromPlaylist({
981 url: server.url,
982 token: server.accessToken,
983 playlistId: playlistServer2Id1,
984 playlistElementId: element1
985 })
986
987 const names2 = await getPlaylistNames()
988 expect(names2[ 0 ]).to.equal('playlist 2')
989 expect(names2[ 1 ]).to.equal('playlist 3 updated')
990
991 await removeVideoFromPlaylist({
992 url: server.url,
993 token: server.accessToken,
994 playlistId: playlistServer2Id2,
995 playlistElementId: element2
996 })
997
998 const names3 = await getPlaylistNames()
999 expect(names3[ 0 ]).to.equal('playlist 3 updated')
1000 expect(names3[ 1 ]).to.equal('playlist 2')
1001 })
1002
1003 it('Should delete some elements', async function () {
1004 this.timeout(30000)
1005
1006 await removeVideoFromPlaylist({
1007 url: servers[ 0 ].url,
1008 token: servers[ 0 ].accessToken,
1009 playlistId: playlistServer1Id,
1010 playlistElementId: playlistElementServer1Video4
1011 })
1012
1013 await removeVideoFromPlaylist({
1014 url: servers[ 0 ].url,
1015 token: servers[ 0 ].accessToken,
1016 playlistId: playlistServer1Id,
1017 playlistElementId: playlistElementNSFW
1018 })
1019
1020 await waitJobs(servers)
1021
1022 for (const server of servers) {
1023 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1024
1025 expect(res.body.total).to.equal(4)
1026
1027 const elements: VideoPlaylistElement[] = res.body.data
1028 expect(elements).to.have.lengthOf(4)
1029
1030 expect(elements[ 0 ].video.name).to.equal('video 0 server 1')
1031 expect(elements[ 0 ].position).to.equal(1)
1032
1033 expect(elements[ 1 ].video.name).to.equal('video 2 server 3')
1034 expect(elements[ 1 ].position).to.equal(2)
1035
1036 expect(elements[ 2 ].video.name).to.equal('video 1 server 3')
1037 expect(elements[ 2 ].position).to.equal(3)
1038
1039 expect(elements[ 3 ].video.name).to.equal('video 4 server 1')
1040 expect(elements[ 3 ].position).to.equal(4)
1041 }
1042 })
1043
1044 it('Should be able to create a public playlist, and set it to private', async function () {
1045 this.timeout(30000)
1046
1047 const res = await createVideoPlaylist({
1048 url: servers[ 0 ].url,
1049 token: servers[ 0 ].accessToken,
1050 playlistAttrs: {
1051 displayName: 'my super public playlist',
1052 privacy: VideoPlaylistPrivacy.PUBLIC,
1053 videoChannelId: servers[ 0 ].videoChannel.id
1054 }
1055 })
1056 const videoPlaylistIds = res.body.videoPlaylist
1057
1058 await waitJobs(servers)
1059
1060 for (const server of servers) {
1061 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 200)
1062 }
1063
1064 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1065 await updateVideoPlaylist({ url: servers[ 0 ].url, token: servers[ 0 ].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1066
1067 await waitJobs(servers)
1068
1069 for (const server of [ servers[ 1 ], servers[ 2 ] ]) {
1070 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, 404)
1071 }
1072 await getVideoPlaylist(servers[ 0 ].url, videoPlaylistIds.uuid, 401)
1073
1074 await getVideoPlaylistWithToken(servers[ 0 ].url, servers[ 0 ].accessToken, videoPlaylistIds.uuid, 200)
1075 })
1076 })
1077
1078 describe('Playlist deletion', function () {
1079
1080 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1081 this.timeout(30000)
1082
1083 await deleteVideoPlaylist(servers[ 0 ].url, servers[ 0 ].accessToken, playlistServer1Id)
1084
1085 await waitJobs(servers)
1086
1087 for (const server of servers) {
1088 await getVideoPlaylist(server.url, playlistServer1UUID, 404)
1089 }
1090 })
1091
1092 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1093 this.timeout(30000)
1094
1095 for (const server of servers) {
1096 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1097 }
1098 })
1099
1100 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1101 this.timeout(30000)
1102
1103 const finder = data => data.find(p => p.displayName === 'my super playlist')
1104
1105 {
1106 const res = await getVideoPlaylistsList(servers[ 2 ].url, 0, 5)
1107 expect(res.body.total).to.equal(3)
1108 expect(finder(res.body.data)).to.not.be.undefined
1109 }
1110
1111 await unfollow(servers[ 2 ].url, servers[ 2 ].accessToken, servers[ 0 ])
1112
1113 {
1114 const res = await getVideoPlaylistsList(servers[ 2 ].url, 0, 5)
1115 expect(res.body.total).to.equal(1)
1116
1117 expect(finder(res.body.data)).to.be.undefined
1118 }
1119 })
1120
1121 it('Should delete a channel and put the associated playlist in private mode', async function () {
1122 this.timeout(30000)
1123
1124 const res = await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'super_channel', displayName: 'super channel' })
1125 const videoChannelId = res.body.videoChannel.id
1126
1127 const res2 = await createVideoPlaylist({
1128 url: servers[ 0 ].url,
1129 token: servers[ 0 ].accessToken,
1130 playlistAttrs: {
1131 displayName: 'channel playlist',
1132 privacy: VideoPlaylistPrivacy.PUBLIC,
1133 videoChannelId
1134 }
1135 })
1136 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1137
1138 await waitJobs(servers)
1139
1140 await deleteVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, 'super_channel')
1141
1142 await waitJobs(servers)
1143
1144 const res3 = await getVideoPlaylistWithToken(servers[ 0 ].url, servers[ 0 ].accessToken, videoPlaylistUUID)
1145 expect(res3.body.displayName).to.equal('channel playlist')
1146 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1147
1148 await getVideoPlaylist(servers[ 1 ].url, videoPlaylistUUID, 404)
1149 })
1150
1151 it('Should delete an account and delete its playlists', async function () {
1152 this.timeout(30000)
1153
1154 const user = { username: 'user_1', password: 'password' }
1155 const res = await createUser({
1156 url: servers[ 0 ].url,
1157 accessToken: servers[ 0 ].accessToken,
1158 username: user.username,
1159 password: user.password
1160 })
1161
1162 const userId = res.body.user.id
1163 const userAccessToken = await userLogin(servers[ 0 ], user)
1164
1165 const resChannel = await getMyUserInformation(servers[ 0 ].url, userAccessToken)
1166 const userChannel = (resChannel.body as User).videoChannels[ 0 ]
1167
1168 await createVideoPlaylist({
1169 url: servers[ 0 ].url,
1170 token: userAccessToken,
1171 playlistAttrs: {
1172 displayName: 'playlist to be deleted',
1173 privacy: VideoPlaylistPrivacy.PUBLIC,
1174 videoChannelId: userChannel.id
1175 }
1176 })
1177
1178 await waitJobs(servers)
1179
1180 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1181
1182 {
1183 for (const server of [ servers[ 0 ], servers[ 1 ] ]) {
1184 const res = await getVideoPlaylistsList(server.url, 0, 15)
1185 expect(finder(res.body.data)).to.not.be.undefined
1186 }
1187 }
1188
1189 await removeUser(servers[ 0 ].url, userId, servers[ 0 ].accessToken)
1190 await waitJobs(servers)
1191
1192 {
1193 for (const server of [ servers[ 0 ], servers[ 1 ] ]) {
1194 const res = await getVideoPlaylistsList(server.url, 0, 15)
1195 expect(finder(res.body.data)).to.be.undefined
1196 }
1197 }
1198 })
1199 })
1200
1201 after(async function () {
1202 await cleanupTests(servers)
1203 })
1204 })