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