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