]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/video-playlists.ts
Support short uuid for GET video/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 '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 { VideoPlaylistCreateResult, 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
432 describe('Playlist rights', function () {
433 let unlistedPlaylist: VideoPlaylistCreateResult
434 let privatePlaylist: VideoPlaylistCreateResult
435
436 before(async function () {
437 this.timeout(30000)
438
439 {
440 const res = await createVideoPlaylist({
441 url: servers[1].url,
442 token: servers[1].accessToken,
443 playlistAttrs: {
444 displayName: 'playlist unlisted',
445 privacy: VideoPlaylistPrivacy.UNLISTED,
446 videoChannelId: servers[1].videoChannel.id
447 }
448 })
449 unlistedPlaylist = res.body.videoPlaylist
450 }
451
452 {
453 const res = await createVideoPlaylist({
454 url: servers[1].url,
455 token: servers[1].accessToken,
456 playlistAttrs: {
457 displayName: 'playlist private',
458 privacy: VideoPlaylistPrivacy.PRIVATE
459 }
460 })
461 privatePlaylist = res.body.videoPlaylist
462 }
463
464 await waitJobs(servers)
465 await wait(3000)
466 })
467
468 it('Should not list unlisted or private playlists', async function () {
469 for (const server of servers) {
470 const results = [
471 await getAccountPlaylistsList(server.url, 'root@localhost:' + servers[1].port, 0, 5, '-createdAt'),
472 await getVideoPlaylistsList(server.url, 0, 2, '-createdAt')
473 ]
474
475 expect(results[0].body.total).to.equal(2)
476 expect(results[1].body.total).to.equal(3)
477
478 for (const res of results) {
479 const data: VideoPlaylist[] = res.body.data
480 expect(data).to.have.lengthOf(2)
481 expect(data[0].displayName).to.equal('playlist 3')
482 expect(data[1].displayName).to.equal('playlist 2')
483 }
484 }
485 })
486
487 it('Should not get unlisted playlist using only the id', async function () {
488 await getVideoPlaylist(servers[1].url, unlistedPlaylist.id, 404)
489 })
490
491 it('Should get unlisted plyaylist using uuid or shortUUID', async function () {
492 await getVideoPlaylist(servers[1].url, unlistedPlaylist.uuid)
493 await getVideoPlaylist(servers[1].url, unlistedPlaylist.shortUUID)
494 })
495
496 it('Should not get private playlist without token', async function () {
497 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
498 await getVideoPlaylist(servers[1].url, id, 401)
499 }
500 })
501
502 it('Should get private playlist with a token', async function () {
503 for (const id of [ privatePlaylist.id, privatePlaylist.uuid, privatePlaylist.shortUUID ]) {
504 await getVideoPlaylistWithToken(servers[1].url, servers[1].accessToken, id)
505 }
506 })
507 })
508
509 describe('Update playlists', function () {
510
511 it('Should update a playlist', async function () {
512 this.timeout(30000)
513
514 await updateVideoPlaylist({
515 url: servers[1].url,
516 token: servers[1].accessToken,
517 playlistAttrs: {
518 displayName: 'playlist 3 updated',
519 description: 'description updated',
520 privacy: VideoPlaylistPrivacy.UNLISTED,
521 thumbnailfile: 'thumbnail.jpg',
522 videoChannelId: servers[1].videoChannel.id
523 },
524 playlistId: playlistServer2Id2
525 })
526
527 await waitJobs(servers)
528
529 for (const server of servers) {
530 const res = await getVideoPlaylist(server.url, playlistServer2UUID2)
531 const playlist: VideoPlaylist = res.body
532
533 expect(playlist.displayName).to.equal('playlist 3 updated')
534 expect(playlist.description).to.equal('description updated')
535
536 expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.UNLISTED)
537 expect(playlist.privacy.label).to.equal('Unlisted')
538
539 expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR)
540 expect(playlist.type.label).to.equal('Regular')
541
542 expect(playlist.videosLength).to.equal(2)
543
544 expect(playlist.ownerAccount.name).to.equal('root')
545 expect(playlist.ownerAccount.displayName).to.equal('root')
546 expect(playlist.videoChannel.name).to.equal('root_channel')
547 expect(playlist.videoChannel.displayName).to.equal('Main root channel')
548 }
549 })
550 })
551
552 describe('Element timestamps', function () {
553
554 it('Should create a playlist containing different startTimestamp/endTimestamp videos', async function () {
555 this.timeout(30000)
556
557 const addVideo = (elementAttrs: any) => {
558 return addVideoInPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: playlistServer1Id, elementAttrs })
559 }
560
561 const res = await createVideoPlaylist({
562 url: servers[0].url,
563 token: servers[0].accessToken,
564 playlistAttrs: {
565 displayName: 'playlist 4',
566 privacy: VideoPlaylistPrivacy.PUBLIC,
567 videoChannelId: servers[0].videoChannel.id
568 }
569 })
570
571 playlistServer1Id = res.body.videoPlaylist.id
572 playlistServer1UUID = res.body.videoPlaylist.uuid
573
574 await addVideo({ videoId: servers[0].videos[0].uuid, startTimestamp: 15, stopTimestamp: 28 })
575 await addVideo({ videoId: servers[2].videos[1].uuid, startTimestamp: 35 })
576 await addVideo({ videoId: servers[2].videos[2].uuid })
577 {
578 const res = await addVideo({ videoId: servers[0].videos[3].uuid, stopTimestamp: 35 })
579 playlistElementServer1Video4 = res.body.videoPlaylistElement.id
580 }
581
582 {
583 const res = await addVideo({ videoId: servers[0].videos[4].uuid, startTimestamp: 45, stopTimestamp: 60 })
584 playlistElementServer1Video5 = res.body.videoPlaylistElement.id
585 }
586
587 {
588 const res = await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 5 })
589 playlistElementNSFW = res.body.videoPlaylistElement.id
590
591 await addVideo({ videoId: nsfwVideoServer1, startTimestamp: 4 })
592 await addVideo({ videoId: nsfwVideoServer1 })
593 }
594
595 await waitJobs(servers)
596 })
597
598 it('Should correctly list playlist videos', async function () {
599 this.timeout(30000)
600
601 for (const server of servers) {
602 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
603
604 expect(res.body.total).to.equal(8)
605
606 const videoElements: VideoPlaylistElement[] = res.body.data
607 expect(videoElements).to.have.lengthOf(8)
608
609 expect(videoElements[0].video.name).to.equal('video 0 server 1')
610 expect(videoElements[0].position).to.equal(1)
611 expect(videoElements[0].startTimestamp).to.equal(15)
612 expect(videoElements[0].stopTimestamp).to.equal(28)
613
614 expect(videoElements[1].video.name).to.equal('video 1 server 3')
615 expect(videoElements[1].position).to.equal(2)
616 expect(videoElements[1].startTimestamp).to.equal(35)
617 expect(videoElements[1].stopTimestamp).to.be.null
618
619 expect(videoElements[2].video.name).to.equal('video 2 server 3')
620 expect(videoElements[2].position).to.equal(3)
621 expect(videoElements[2].startTimestamp).to.be.null
622 expect(videoElements[2].stopTimestamp).to.be.null
623
624 expect(videoElements[3].video.name).to.equal('video 3 server 1')
625 expect(videoElements[3].position).to.equal(4)
626 expect(videoElements[3].startTimestamp).to.be.null
627 expect(videoElements[3].stopTimestamp).to.equal(35)
628
629 expect(videoElements[4].video.name).to.equal('video 4 server 1')
630 expect(videoElements[4].position).to.equal(5)
631 expect(videoElements[4].startTimestamp).to.equal(45)
632 expect(videoElements[4].stopTimestamp).to.equal(60)
633
634 expect(videoElements[5].video.name).to.equal('NSFW video')
635 expect(videoElements[5].position).to.equal(6)
636 expect(videoElements[5].startTimestamp).to.equal(5)
637 expect(videoElements[5].stopTimestamp).to.be.null
638
639 expect(videoElements[6].video.name).to.equal('NSFW video')
640 expect(videoElements[6].position).to.equal(7)
641 expect(videoElements[6].startTimestamp).to.equal(4)
642 expect(videoElements[6].stopTimestamp).to.be.null
643
644 expect(videoElements[7].video.name).to.equal('NSFW video')
645 expect(videoElements[7].position).to.equal(8)
646 expect(videoElements[7].startTimestamp).to.be.null
647 expect(videoElements[7].stopTimestamp).to.be.null
648
649 const res3 = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 2)
650 expect(res3.body.data).to.have.lengthOf(2)
651 }
652 })
653 })
654
655 describe('Element type', function () {
656 let groupUser1: ServerInfo[]
657 let groupWithoutToken1: ServerInfo[]
658 let group1: ServerInfo[]
659 let group2: ServerInfo[]
660
661 let video1: string
662 let video2: string
663 let video3: string
664
665 before(async function () {
666 this.timeout(60000)
667
668 groupUser1 = [ Object.assign({}, servers[0], { accessToken: userAccessTokenServer1 }) ]
669 groupWithoutToken1 = [ Object.assign({}, servers[0], { accessToken: undefined }) ]
670 group1 = [ servers[0] ]
671 group2 = [ servers[1], servers[2] ]
672
673 const res = await createVideoPlaylist({
674 url: servers[0].url,
675 token: userAccessTokenServer1,
676 playlistAttrs: {
677 displayName: 'playlist 56',
678 privacy: VideoPlaylistPrivacy.PUBLIC,
679 videoChannelId: servers[0].videoChannel.id
680 }
681 })
682
683 const playlistServer1Id2 = res.body.videoPlaylist.id
684 playlistServer1UUID2 = res.body.videoPlaylist.uuid
685
686 const addVideo = (elementAttrs: any) => {
687 return addVideoInPlaylist({ url: servers[0].url, token: userAccessTokenServer1, playlistId: playlistServer1Id2, elementAttrs })
688 }
689
690 video1 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 89', token: userAccessTokenServer1 })).uuid
691 video2 = (await uploadVideoAndGetId({ server: servers[1], videoName: 'video 90' })).uuid
692 video3 = (await uploadVideoAndGetId({ server: servers[0], videoName: 'video 91', nsfw: true })).uuid
693
694 await waitJobs(servers)
695
696 await addVideo({ videoId: video1, startTimestamp: 15, stopTimestamp: 28 })
697 await addVideo({ videoId: video2, startTimestamp: 35 })
698 await addVideo({ videoId: video3 })
699
700 await waitJobs(servers)
701 })
702
703 it('Should update the element type if the video is private', async function () {
704 this.timeout(20000)
705
706 const name = 'video 89'
707 const position = 1
708
709 {
710 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PRIVATE })
711 await waitJobs(servers)
712
713 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
714 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
715 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.PRIVATE, position, name, 3)
716 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
717 }
718
719 {
720 await updateVideo(servers[0].url, servers[0].accessToken, video1, { privacy: VideoPrivacy.PUBLIC })
721 await waitJobs(servers)
722
723 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
724 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
725 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
726 // We deleted the video, so even if we recreated it, the old entry is still deleted
727 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
728 }
729 })
730
731 it('Should update the element type if the video is blacklisted', async function () {
732 this.timeout(20000)
733
734 const name = 'video 89'
735 const position = 1
736
737 {
738 await addVideoToBlacklist(servers[0].url, servers[0].accessToken, video1, 'reason', true)
739 await waitJobs(servers)
740
741 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
742 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
743 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
744 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
745 }
746
747 {
748 await removeVideoFromBlacklist(servers[0].url, servers[0].accessToken, video1)
749 await waitJobs(servers)
750
751 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
752 await checkPlaylistElementType(groupWithoutToken1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
753 await checkPlaylistElementType(group1, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
754 // We deleted the video (because unfederated), so even if we recreated it, the old entry is still deleted
755 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.DELETED, position, name, 3)
756 }
757 })
758
759 it('Should update the element type if the account or server of the video is blocked', async function () {
760 this.timeout(90000)
761
762 const name = 'video 90'
763 const position = 2
764
765 {
766 await addAccountToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
767 await waitJobs(servers)
768
769 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
770 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
771
772 await removeAccountFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'root@localhost:' + servers[1].port)
773 await waitJobs(servers)
774
775 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
776 }
777
778 {
779 await addServerToAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
780 await waitJobs(servers)
781
782 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
783 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
784
785 await removeServerFromAccountBlocklist(servers[0].url, userAccessTokenServer1, 'localhost:' + servers[1].port)
786 await waitJobs(servers)
787
788 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
789 }
790
791 {
792 await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
793 await waitJobs(servers)
794
795 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
796 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
797
798 await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, 'root@localhost:' + servers[1].port)
799 await waitJobs(servers)
800
801 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
802 }
803
804 {
805 await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
806 await waitJobs(servers)
807
808 await checkPlaylistElementType(groupUser1, playlistServer1UUID2, VideoPlaylistElementType.UNAVAILABLE, position, name, 3)
809 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
810
811 await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, 'localhost:' + servers[1].port)
812 await waitJobs(servers)
813
814 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
815 }
816 })
817
818 it('Should hide the video if it is NSFW', async function () {
819 const res = await getPlaylistVideos(servers[0].url, userAccessTokenServer1, playlistServer1UUID2, 0, 10, { nsfw: false })
820 expect(res.body.total).to.equal(3)
821
822 const elements: VideoPlaylistElement[] = res.body.data
823 const element = elements.find(e => e.position === 3)
824
825 expect(element).to.exist
826 expect(element.video).to.be.null
827 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
828 })
829
830 })
831
832 describe('Managing playlist elements', function () {
833
834 it('Should reorder the playlist', async function () {
835 this.timeout(30000)
836
837 {
838 await reorderVideosPlaylist({
839 url: servers[0].url,
840 token: servers[0].accessToken,
841 playlistId: playlistServer1Id,
842 elementAttrs: {
843 startPosition: 2,
844 insertAfterPosition: 3
845 }
846 })
847
848 await waitJobs(servers)
849
850 for (const server of servers) {
851 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
852 const names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
853
854 expect(names).to.deep.equal([
855 'video 0 server 1',
856 'video 2 server 3',
857 'video 1 server 3',
858 'video 3 server 1',
859 'video 4 server 1',
860 'NSFW video',
861 'NSFW video',
862 'NSFW video'
863 ])
864 }
865 }
866
867 {
868 await reorderVideosPlaylist({
869 url: servers[0].url,
870 token: servers[0].accessToken,
871 playlistId: playlistServer1Id,
872 elementAttrs: {
873 startPosition: 1,
874 reorderLength: 3,
875 insertAfterPosition: 4
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 names = (res.body.data as VideoPlaylistElement[]).map(v => v.video.name)
884
885 expect(names).to.deep.equal([
886 'video 3 server 1',
887 'video 0 server 1',
888 'video 2 server 3',
889 'video 1 server 3',
890 'video 4 server 1',
891 'NSFW video',
892 'NSFW video',
893 'NSFW video'
894 ])
895 }
896 }
897
898 {
899 await reorderVideosPlaylist({
900 url: servers[0].url,
901 token: servers[0].accessToken,
902 playlistId: playlistServer1Id,
903 elementAttrs: {
904 startPosition: 6,
905 insertAfterPosition: 3
906 }
907 })
908
909 await waitJobs(servers)
910
911 for (const server of servers) {
912 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
913 const elements: VideoPlaylistElement[] = res.body.data
914 const names = elements.map(v => v.video.name)
915
916 expect(names).to.deep.equal([
917 'video 3 server 1',
918 'video 0 server 1',
919 'video 2 server 3',
920 'NSFW video',
921 'video 1 server 3',
922 'video 4 server 1',
923 'NSFW video',
924 'NSFW video'
925 ])
926
927 for (let i = 1; i <= elements.length; i++) {
928 expect(elements[i - 1].position).to.equal(i)
929 }
930 }
931 }
932 })
933
934 it('Should update startTimestamp/endTimestamp of some elements', async function () {
935 this.timeout(30000)
936
937 await updateVideoPlaylistElement({
938 url: servers[0].url,
939 token: servers[0].accessToken,
940 playlistId: playlistServer1Id,
941 playlistElementId: playlistElementServer1Video4,
942 elementAttrs: {
943 startTimestamp: 1
944 }
945 })
946
947 await updateVideoPlaylistElement({
948 url: servers[0].url,
949 token: servers[0].accessToken,
950 playlistId: playlistServer1Id,
951 playlistElementId: playlistElementServer1Video5,
952 elementAttrs: {
953 stopTimestamp: null
954 }
955 })
956
957 await waitJobs(servers)
958
959 for (const server of servers) {
960 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
961 const elements: VideoPlaylistElement[] = res.body.data
962
963 expect(elements[0].video.name).to.equal('video 3 server 1')
964 expect(elements[0].position).to.equal(1)
965 expect(elements[0].startTimestamp).to.equal(1)
966 expect(elements[0].stopTimestamp).to.equal(35)
967
968 expect(elements[5].video.name).to.equal('video 4 server 1')
969 expect(elements[5].position).to.equal(6)
970 expect(elements[5].startTimestamp).to.equal(45)
971 expect(elements[5].stopTimestamp).to.be.null
972 }
973 })
974
975 it('Should check videos existence in my playlist', async function () {
976 const videoIds = [
977 servers[0].videos[0].id,
978 42000,
979 servers[0].videos[3].id,
980 43000,
981 servers[0].videos[4].id
982 ]
983 const res = await doVideosExistInMyPlaylist(servers[0].url, servers[0].accessToken, videoIds)
984 const obj = res.body as VideoExistInPlaylist
985
986 {
987 const elem = obj[servers[0].videos[0].id]
988 expect(elem).to.have.lengthOf(1)
989 expect(elem[0].playlistElementId).to.exist
990 expect(elem[0].playlistId).to.equal(playlistServer1Id)
991 expect(elem[0].startTimestamp).to.equal(15)
992 expect(elem[0].stopTimestamp).to.equal(28)
993 }
994
995 {
996 const elem = obj[servers[0].videos[3].id]
997 expect(elem).to.have.lengthOf(1)
998 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
999 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1000 expect(elem[0].startTimestamp).to.equal(1)
1001 expect(elem[0].stopTimestamp).to.equal(35)
1002 }
1003
1004 {
1005 const elem = obj[servers[0].videos[4].id]
1006 expect(elem).to.have.lengthOf(1)
1007 expect(elem[0].playlistId).to.equal(playlistServer1Id)
1008 expect(elem[0].startTimestamp).to.equal(45)
1009 expect(elem[0].stopTimestamp).to.equal(null)
1010 }
1011
1012 expect(obj[42000]).to.have.lengthOf(0)
1013 expect(obj[43000]).to.have.lengthOf(0)
1014 })
1015
1016 it('Should automatically update updatedAt field of playlists', async function () {
1017 const server = servers[1]
1018 const videoId = servers[1].videos[5].id
1019
1020 async function getPlaylistNames () {
1021 const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, undefined, '-updatedAt')
1022
1023 return (res.body.data as VideoPlaylist[]).map(p => p.displayName)
1024 }
1025
1026 const elementAttrs = { videoId }
1027 const res1 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id1, elementAttrs })
1028 const res2 = await addVideoInPlaylist({ url: server.url, token: server.accessToken, playlistId: playlistServer2Id2, elementAttrs })
1029
1030 const element1 = res1.body.videoPlaylistElement.id
1031 const element2 = res2.body.videoPlaylistElement.id
1032
1033 const names1 = await getPlaylistNames()
1034 expect(names1[0]).to.equal('playlist 3 updated')
1035 expect(names1[1]).to.equal('playlist 2')
1036
1037 await removeVideoFromPlaylist({
1038 url: server.url,
1039 token: server.accessToken,
1040 playlistId: playlistServer2Id1,
1041 playlistElementId: element1
1042 })
1043
1044 const names2 = await getPlaylistNames()
1045 expect(names2[0]).to.equal('playlist 2')
1046 expect(names2[1]).to.equal('playlist 3 updated')
1047
1048 await removeVideoFromPlaylist({
1049 url: server.url,
1050 token: server.accessToken,
1051 playlistId: playlistServer2Id2,
1052 playlistElementId: element2
1053 })
1054
1055 const names3 = await getPlaylistNames()
1056 expect(names3[0]).to.equal('playlist 3 updated')
1057 expect(names3[1]).to.equal('playlist 2')
1058 })
1059
1060 it('Should delete some elements', async function () {
1061 this.timeout(30000)
1062
1063 await removeVideoFromPlaylist({
1064 url: servers[0].url,
1065 token: servers[0].accessToken,
1066 playlistId: playlistServer1Id,
1067 playlistElementId: playlistElementServer1Video4
1068 })
1069
1070 await removeVideoFromPlaylist({
1071 url: servers[0].url,
1072 token: servers[0].accessToken,
1073 playlistId: playlistServer1Id,
1074 playlistElementId: playlistElementNSFW
1075 })
1076
1077 await waitJobs(servers)
1078
1079 for (const server of servers) {
1080 const res = await getPlaylistVideos(server.url, server.accessToken, playlistServer1UUID, 0, 10)
1081
1082 expect(res.body.total).to.equal(6)
1083
1084 const elements: VideoPlaylistElement[] = res.body.data
1085 expect(elements).to.have.lengthOf(6)
1086
1087 expect(elements[0].video.name).to.equal('video 0 server 1')
1088 expect(elements[0].position).to.equal(1)
1089
1090 expect(elements[1].video.name).to.equal('video 2 server 3')
1091 expect(elements[1].position).to.equal(2)
1092
1093 expect(elements[2].video.name).to.equal('video 1 server 3')
1094 expect(elements[2].position).to.equal(3)
1095
1096 expect(elements[3].video.name).to.equal('video 4 server 1')
1097 expect(elements[3].position).to.equal(4)
1098
1099 expect(elements[4].video.name).to.equal('NSFW video')
1100 expect(elements[4].position).to.equal(5)
1101
1102 expect(elements[5].video.name).to.equal('NSFW video')
1103 expect(elements[5].position).to.equal(6)
1104 }
1105 })
1106
1107 it('Should be able to create a public playlist, and set it to private', async function () {
1108 this.timeout(30000)
1109
1110 const res = await createVideoPlaylist({
1111 url: servers[0].url,
1112 token: servers[0].accessToken,
1113 playlistAttrs: {
1114 displayName: 'my super public playlist',
1115 privacy: VideoPlaylistPrivacy.PUBLIC,
1116 videoChannelId: servers[0].videoChannel.id
1117 }
1118 })
1119 const videoPlaylistIds = res.body.videoPlaylist
1120
1121 await waitJobs(servers)
1122
1123 for (const server of servers) {
1124 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1125 }
1126
1127 const playlistAttrs = { privacy: VideoPlaylistPrivacy.PRIVATE }
1128 await updateVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistId: videoPlaylistIds.id, playlistAttrs })
1129
1130 await waitJobs(servers)
1131
1132 for (const server of [ servers[1], servers[2] ]) {
1133 await getVideoPlaylist(server.url, videoPlaylistIds.uuid, HttpStatusCode.NOT_FOUND_404)
1134 }
1135 await getVideoPlaylist(servers[0].url, videoPlaylistIds.uuid, HttpStatusCode.UNAUTHORIZED_401)
1136
1137 await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistIds.uuid, HttpStatusCode.OK_200)
1138 })
1139 })
1140
1141 describe('Playlist deletion', function () {
1142
1143 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1144 this.timeout(30000)
1145
1146 await deleteVideoPlaylist(servers[0].url, servers[0].accessToken, playlistServer1Id)
1147
1148 await waitJobs(servers)
1149
1150 for (const server of servers) {
1151 await getVideoPlaylist(server.url, playlistServer1UUID, HttpStatusCode.NOT_FOUND_404)
1152 }
1153 })
1154
1155 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1156 this.timeout(30000)
1157
1158 for (const server of servers) {
1159 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server.internalServerNumber)
1160 }
1161 })
1162
1163 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1164 this.timeout(30000)
1165
1166 const finder = data => data.find(p => p.displayName === 'my super playlist')
1167
1168 {
1169 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1170 expect(res.body.total).to.equal(3)
1171 expect(finder(res.body.data)).to.not.be.undefined
1172 }
1173
1174 await unfollow(servers[2].url, servers[2].accessToken, servers[0])
1175
1176 {
1177 const res = await getVideoPlaylistsList(servers[2].url, 0, 5)
1178 expect(res.body.total).to.equal(1)
1179
1180 expect(finder(res.body.data)).to.be.undefined
1181 }
1182 })
1183
1184 it('Should delete a channel and put the associated playlist in private mode', async function () {
1185 this.timeout(30000)
1186
1187 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, { name: 'super_channel', displayName: 'super channel' })
1188 const videoChannelId = res.body.videoChannel.id
1189
1190 const res2 = await createVideoPlaylist({
1191 url: servers[0].url,
1192 token: servers[0].accessToken,
1193 playlistAttrs: {
1194 displayName: 'channel playlist',
1195 privacy: VideoPlaylistPrivacy.PUBLIC,
1196 videoChannelId
1197 }
1198 })
1199 const videoPlaylistUUID = res2.body.videoPlaylist.uuid
1200
1201 await waitJobs(servers)
1202
1203 await deleteVideoChannel(servers[0].url, servers[0].accessToken, 'super_channel')
1204
1205 await waitJobs(servers)
1206
1207 const res3 = await getVideoPlaylistWithToken(servers[0].url, servers[0].accessToken, videoPlaylistUUID)
1208 expect(res3.body.displayName).to.equal('channel playlist')
1209 expect(res3.body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1210
1211 await getVideoPlaylist(servers[1].url, videoPlaylistUUID, HttpStatusCode.NOT_FOUND_404)
1212 })
1213
1214 it('Should delete an account and delete its playlists', async function () {
1215 this.timeout(30000)
1216
1217 const user = { username: 'user_1', password: 'password' }
1218 const res = await createUser({
1219 url: servers[0].url,
1220 accessToken: servers[0].accessToken,
1221 username: user.username,
1222 password: user.password
1223 })
1224
1225 const userId = res.body.user.id
1226 const userAccessToken = await userLogin(servers[0], user)
1227
1228 const resChannel = await getMyUserInformation(servers[0].url, userAccessToken)
1229 const userChannel = (resChannel.body as User).videoChannels[0]
1230
1231 await createVideoPlaylist({
1232 url: servers[0].url,
1233 token: userAccessToken,
1234 playlistAttrs: {
1235 displayName: 'playlist to be deleted',
1236 privacy: VideoPlaylistPrivacy.PUBLIC,
1237 videoChannelId: userChannel.id
1238 }
1239 })
1240
1241 await waitJobs(servers)
1242
1243 const finder = data => data.find(p => p.displayName === 'playlist to be deleted')
1244
1245 {
1246 for (const server of [ servers[0], servers[1] ]) {
1247 const res = await getVideoPlaylistsList(server.url, 0, 15)
1248 expect(finder(res.body.data)).to.not.be.undefined
1249 }
1250 }
1251
1252 await removeUser(servers[0].url, userId, servers[0].accessToken)
1253 await waitJobs(servers)
1254
1255 {
1256 for (const server of [ servers[0], servers[1] ]) {
1257 const res = await getVideoPlaylistsList(server.url, 0, 15)
1258 expect(finder(res.body.data)).to.be.undefined
1259 }
1260 }
1261 })
1262 })
1263
1264 after(async function () {
1265 await cleanupTests(servers)
1266 })
1267 })