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