]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/tests/api/videos/video-playlists.ts
Merge branch 'release/4.3.0' into develop
[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 {
7 HttpStatusCode,
8 VideoPlaylist,
9 VideoPlaylistCreateResult,
10 VideoPlaylistElementType,
11 VideoPlaylistPrivacy,
12 VideoPlaylistType,
13 VideoPrivacy
14 } from '@shared/models'
15 import {
16 cleanupTests,
17 createMultipleServers,
18 doubleFollow,
19 PeerTubeServer,
20 PlaylistsCommand,
21 setAccessTokensToServers,
22 setDefaultAccountAvatar,
23 setDefaultVideoChannel,
24 waitJobs
25 } from '@shared/server-commands'
26 import { uuidToShort } from '@shared/extra-utils'
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@localhost:' + servers[1].port, 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@localhost:' + servers[1].port })
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@localhost:' + servers[1].port })
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: 'localhost:' + servers[1].port })
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: 'localhost:' + servers[1].port })
724 await waitJobs(servers)
725
726 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
727 }
728
729 {
730 await command.addToServerBlocklist({ account: 'root@localhost:' + servers[1].port })
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@localhost:' + servers[1].port })
737 await waitJobs(servers)
738
739 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
740 }
741
742 {
743 await command.addToServerBlocklist({ server: 'localhost:' + servers[1].port })
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: 'localhost:' + servers[1].port })
750 await waitJobs(servers)
751
752 await checkPlaylistElementType(group2, playlistServer1UUID2, VideoPlaylistElementType.REGULAR, position, name, 3)
753 }
754 })
755
756 it('Should hide the video if it is NSFW', async function () {
757 const body = await commands[0].listVideos({ token: userTokenServer1, playlistId: playlistServer1UUID2, query: { nsfw: 'false' } })
758 expect(body.total).to.equal(3)
759
760 const elements = body.data
761 const element = elements.find(e => e.position === 3)
762
763 expect(element).to.exist
764 expect(element.video).to.be.null
765 expect(element.type).to.equal(VideoPlaylistElementType.UNAVAILABLE)
766 })
767
768 })
769
770 describe('Managing playlist elements', function () {
771
772 it('Should reorder the playlist', async function () {
773 this.timeout(30000)
774
775 {
776 await commands[0].reorderElements({
777 playlistId: playlistServer1Id,
778 attributes: {
779 startPosition: 2,
780 insertAfterPosition: 3
781 }
782 })
783
784 await waitJobs(servers)
785
786 for (const server of servers) {
787 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
788 const names = body.data.map(v => v.video.name)
789
790 expect(names).to.deep.equal([
791 'video 0 server 1',
792 'video 2 server 3',
793 'video 1 server 3',
794 'video 3 server 1',
795 'video 4 server 1',
796 'NSFW video',
797 'NSFW video',
798 'NSFW video'
799 ])
800 }
801 }
802
803 {
804 await commands[0].reorderElements({
805 playlistId: playlistServer1Id,
806 attributes: {
807 startPosition: 1,
808 reorderLength: 3,
809 insertAfterPosition: 4
810 }
811 })
812
813 await waitJobs(servers)
814
815 for (const server of servers) {
816 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
817 const names = body.data.map(v => v.video.name)
818
819 expect(names).to.deep.equal([
820 'video 3 server 1',
821 'video 0 server 1',
822 'video 2 server 3',
823 'video 1 server 3',
824 'video 4 server 1',
825 'NSFW video',
826 'NSFW video',
827 'NSFW video'
828 ])
829 }
830 }
831
832 {
833 await commands[0].reorderElements({
834 playlistId: playlistServer1Id,
835 attributes: {
836 startPosition: 6,
837 insertAfterPosition: 3
838 }
839 })
840
841 await waitJobs(servers)
842
843 for (const server of servers) {
844 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
845 const names = elements.map(v => v.video.name)
846
847 expect(names).to.deep.equal([
848 'video 3 server 1',
849 'video 0 server 1',
850 'video 2 server 3',
851 'NSFW video',
852 'video 1 server 3',
853 'video 4 server 1',
854 'NSFW video',
855 'NSFW video'
856 ])
857
858 for (let i = 1; i <= elements.length; i++) {
859 expect(elements[i - 1].position).to.equal(i)
860 }
861 }
862 }
863 })
864
865 it('Should update startTimestamp/endTimestamp of some elements', async function () {
866 this.timeout(30000)
867
868 await commands[0].updateElement({
869 playlistId: playlistServer1Id,
870 elementId: playlistElementServer1Video4,
871 attributes: {
872 startTimestamp: 1
873 }
874 })
875
876 await commands[0].updateElement({
877 playlistId: playlistServer1Id,
878 elementId: playlistElementServer1Video5,
879 attributes: {
880 stopTimestamp: null
881 }
882 })
883
884 await waitJobs(servers)
885
886 for (const server of servers) {
887 const { data: elements } = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
888
889 expect(elements[0].video.name).to.equal('video 3 server 1')
890 expect(elements[0].position).to.equal(1)
891 expect(elements[0].startTimestamp).to.equal(1)
892 expect(elements[0].stopTimestamp).to.equal(35)
893
894 expect(elements[5].video.name).to.equal('video 4 server 1')
895 expect(elements[5].position).to.equal(6)
896 expect(elements[5].startTimestamp).to.equal(45)
897 expect(elements[5].stopTimestamp).to.be.null
898 }
899 })
900
901 it('Should check videos existence in my playlist', async function () {
902 const videoIds = [
903 servers[0].store.videos[0].id,
904 42000,
905 servers[0].store.videos[3].id,
906 43000,
907 servers[0].store.videos[4].id
908 ]
909 const obj = await commands[0].videosExist({ videoIds })
910
911 {
912 const elem = obj[servers[0].store.videos[0].id]
913 expect(elem).to.have.lengthOf(1)
914 expect(elem[0].playlistElementId).to.exist
915 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
916 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
917 expect(elem[0].playlistId).to.equal(playlistServer1Id)
918 expect(elem[0].startTimestamp).to.equal(15)
919 expect(elem[0].stopTimestamp).to.equal(28)
920 }
921
922 {
923 const elem = obj[servers[0].store.videos[3].id]
924 expect(elem).to.have.lengthOf(1)
925 expect(elem[0].playlistElementId).to.equal(playlistElementServer1Video4)
926 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
927 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
928 expect(elem[0].playlistId).to.equal(playlistServer1Id)
929 expect(elem[0].startTimestamp).to.equal(1)
930 expect(elem[0].stopTimestamp).to.equal(35)
931 }
932
933 {
934 const elem = obj[servers[0].store.videos[4].id]
935 expect(elem).to.have.lengthOf(1)
936 expect(elem[0].playlistId).to.equal(playlistServer1Id)
937 expect(elem[0].playlistDisplayName).to.equal(playlistServer1DisplayName)
938 expect(elem[0].playlistShortUUID).to.equal(uuidToShort(playlistServer1UUID))
939 expect(elem[0].startTimestamp).to.equal(45)
940 expect(elem[0].stopTimestamp).to.equal(null)
941 }
942
943 expect(obj[42000]).to.have.lengthOf(0)
944 expect(obj[43000]).to.have.lengthOf(0)
945 })
946
947 it('Should automatically update updatedAt field of playlists', async function () {
948 const server = servers[1]
949 const videoId = servers[1].store.videos[5].id
950
951 async function getPlaylistNames () {
952 const { data } = await server.playlists.listByAccount({ token: server.accessToken, handle: 'root', sort: '-updatedAt' })
953
954 return data.map(p => p.displayName)
955 }
956
957 const attributes = { videoId }
958 const element1 = await server.playlists.addElement({ playlistId: playlistServer2Id1, attributes })
959 const element2 = await server.playlists.addElement({ playlistId: playlistServer2Id2, attributes })
960
961 const names1 = await getPlaylistNames()
962 expect(names1[0]).to.equal('playlist 3 updated')
963 expect(names1[1]).to.equal('playlist 2')
964
965 await server.playlists.removeElement({ playlistId: playlistServer2Id1, elementId: element1.id })
966
967 const names2 = await getPlaylistNames()
968 expect(names2[0]).to.equal('playlist 2')
969 expect(names2[1]).to.equal('playlist 3 updated')
970
971 await server.playlists.removeElement({ playlistId: playlistServer2Id2, elementId: element2.id })
972
973 const names3 = await getPlaylistNames()
974 expect(names3[0]).to.equal('playlist 3 updated')
975 expect(names3[1]).to.equal('playlist 2')
976 })
977
978 it('Should delete some elements', async function () {
979 this.timeout(30000)
980
981 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementServer1Video4 })
982 await commands[0].removeElement({ playlistId: playlistServer1Id, elementId: playlistElementNSFW })
983
984 await waitJobs(servers)
985
986 for (const server of servers) {
987 const body = await server.playlists.listVideos({ playlistId: playlistServer1UUID, start: 0, count: 10 })
988 expect(body.total).to.equal(6)
989
990 const elements = body.data
991 expect(elements).to.have.lengthOf(6)
992
993 expect(elements[0].video.name).to.equal('video 0 server 1')
994 expect(elements[0].position).to.equal(1)
995
996 expect(elements[1].video.name).to.equal('video 2 server 3')
997 expect(elements[1].position).to.equal(2)
998
999 expect(elements[2].video.name).to.equal('video 1 server 3')
1000 expect(elements[2].position).to.equal(3)
1001
1002 expect(elements[3].video.name).to.equal('video 4 server 1')
1003 expect(elements[3].position).to.equal(4)
1004
1005 expect(elements[4].video.name).to.equal('NSFW video')
1006 expect(elements[4].position).to.equal(5)
1007
1008 expect(elements[5].video.name).to.equal('NSFW video')
1009 expect(elements[5].position).to.equal(6)
1010 }
1011 })
1012
1013 it('Should be able to create a public playlist, and set it to private', async function () {
1014 this.timeout(30000)
1015
1016 const videoPlaylistIds = await commands[0].create({
1017 attributes: {
1018 displayName: 'my super public playlist',
1019 privacy: VideoPlaylistPrivacy.PUBLIC,
1020 videoChannelId: servers[0].store.channel.id
1021 }
1022 })
1023
1024 await waitJobs(servers)
1025
1026 for (const server of servers) {
1027 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1028 }
1029
1030 const attributes = { privacy: VideoPlaylistPrivacy.PRIVATE }
1031 await commands[0].update({ playlistId: videoPlaylistIds.id, attributes })
1032
1033 await waitJobs(servers)
1034
1035 for (const server of [ servers[1], servers[2] ]) {
1036 await server.playlists.get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1037 }
1038
1039 await commands[0].get({ playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
1040 await commands[0].get({ token: servers[0].accessToken, playlistId: videoPlaylistIds.uuid, expectedStatus: HttpStatusCode.OK_200 })
1041 })
1042 })
1043
1044 describe('Playlist deletion', function () {
1045
1046 it('Should delete the playlist on server 1 and delete on server 2 and 3', async function () {
1047 this.timeout(30000)
1048
1049 await commands[0].delete({ playlistId: playlistServer1Id })
1050
1051 await waitJobs(servers)
1052
1053 for (const server of servers) {
1054 await server.playlists.get({ playlistId: playlistServer1UUID, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1055 }
1056 })
1057
1058 it('Should have deleted the thumbnail on server 1, 2 and 3', async function () {
1059 this.timeout(30000)
1060
1061 for (const server of servers) {
1062 await checkPlaylistFilesWereRemoved(playlistServer1UUID, server)
1063 }
1064 })
1065
1066 it('Should unfollow servers 1 and 2 and hide their playlists', async function () {
1067 this.timeout(30000)
1068
1069 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'my super playlist')
1070
1071 {
1072 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1073 expect(body.total).to.equal(3)
1074
1075 expect(finder(body.data)).to.not.be.undefined
1076 }
1077
1078 await servers[2].follows.unfollow({ target: servers[0] })
1079
1080 {
1081 const body = await servers[2].playlists.list({ start: 0, count: 5 })
1082 expect(body.total).to.equal(1)
1083
1084 expect(finder(body.data)).to.be.undefined
1085 }
1086 })
1087
1088 it('Should delete a channel and put the associated playlist in private mode', async function () {
1089 this.timeout(30000)
1090
1091 const channel = await servers[0].channels.create({ attributes: { name: 'super_channel', displayName: 'super channel' } })
1092
1093 const playlistCreated = await commands[0].create({
1094 attributes: {
1095 displayName: 'channel playlist',
1096 privacy: VideoPlaylistPrivacy.PUBLIC,
1097 videoChannelId: channel.id
1098 }
1099 })
1100
1101 await waitJobs(servers)
1102
1103 await servers[0].channels.delete({ channelName: 'super_channel' })
1104
1105 await waitJobs(servers)
1106
1107 const body = await commands[0].get({ token: servers[0].accessToken, playlistId: playlistCreated.uuid })
1108 expect(body.displayName).to.equal('channel playlist')
1109 expect(body.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE)
1110
1111 await servers[1].playlists.get({ playlistId: playlistCreated.uuid, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
1112 })
1113
1114 it('Should delete an account and delete its playlists', async function () {
1115 this.timeout(30000)
1116
1117 const { userId, token } = await servers[0].users.generate('user_1')
1118
1119 const { videoChannels } = await servers[0].users.getMyInfo({ token })
1120 const userChannel = videoChannels[0]
1121
1122 await commands[0].create({
1123 attributes: {
1124 displayName: 'playlist to be deleted',
1125 privacy: VideoPlaylistPrivacy.PUBLIC,
1126 videoChannelId: userChannel.id
1127 }
1128 })
1129
1130 await waitJobs(servers)
1131
1132 const finder = (data: VideoPlaylist[]) => data.find(p => p.displayName === 'playlist to be deleted')
1133
1134 {
1135 for (const server of [ servers[0], servers[1] ]) {
1136 const body = await server.playlists.list({ start: 0, count: 15 })
1137
1138 expect(finder(body.data)).to.not.be.undefined
1139 }
1140 }
1141
1142 await servers[0].users.remove({ userId })
1143 await waitJobs(servers)
1144
1145 {
1146 for (const server of [ servers[0], servers[1] ]) {
1147 const body = await server.playlists.list({ start: 0, count: 15 })
1148
1149 expect(finder(body.data)).to.be.undefined
1150 }
1151 }
1152 })
1153 })
1154
1155 after(async function () {
1156 await cleanupTests(servers)
1157 })
1158 })