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