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