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