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