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