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