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