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