diff options
author | Chocobozzz <me@florianbigard.com> | 2023-07-31 14:34:36 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2023-08-11 15:02:33 +0200 |
commit | 3a4992633ee62d5edfbb484d9c6bcb3cf158489d (patch) | |
tree | e4510b39bdac9c318fdb4b47018d08f15368b8f0 /server/tests/api/videos/video-playlists.ts | |
parent | 04d1da5621d25d59bd5fa1543b725c497bf5d9a8 (diff) | |
download | PeerTube-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.ts | 1208 |
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 | |||
3 | import { expect } from 'chai' | ||
4 | import { checkPlaylistFilesWereRemoved, testImageGeneratedByFFmpeg } from '@server/tests/shared' | ||
5 | import { wait } from '@shared/core-utils' | ||
6 | import { uuidToShort } from '@shared/extra-utils' | ||
7 | import { | ||
8 | HttpStatusCode, | ||
9 | VideoPlaylist, | ||
10 | VideoPlaylistCreateResult, | ||
11 | VideoPlaylistElementType, | ||
12 | VideoPlaylistPrivacy, | ||
13 | VideoPlaylistType, | ||
14 | VideoPrivacy | ||
15 | } from '@shared/models' | ||
16 | import { | ||
17 | cleanupTests, | ||
18 | createMultipleServers, | ||
19 | doubleFollow, | ||
20 | PeerTubeServer, | ||
21 | PlaylistsCommand, | ||
22 | setAccessTokensToServers, | ||
23 | setDefaultAccountAvatar, | ||
24 | setDefaultVideoChannel, | ||
25 | waitJobs | ||
26 | } from '@shared/server-commands' | ||
27 | |||
28 | async function checkPlaylistElementType ( | ||
29 | servers: PeerTubeServer[], | ||
30 | playlistId: string, | ||
31 | type: VideoPlaylistElementType, | ||
32 | position: number, | ||
33 | name: string, | ||
34 | total: number | ||
35 | ) { | ||
36 | for (const server of servers) { | ||
37 | const body = await server.playlists.listVideos({ token: server.accessToken, playlistId, start: 0, count: 10 }) | ||
38 | expect(body.total).to.equal(total) | ||
39 | |||
40 | const videoElement = body.data.find(e => e.position === position) | ||
41 | expect(videoElement.type).to.equal(type, 'On server ' + server.url) | ||
42 | |||
43 | if (type === VideoPlaylistElementType.REGULAR) { | ||
44 | expect(videoElement.video).to.not.be.null | ||
45 | expect(videoElement.video.name).to.equal(name) | ||
46 | } else { | ||
47 | expect(videoElement.video).to.be.null | ||
48 | } | ||
49 | } | ||
50 | } | ||
51 | |||
52 | describe('Test video playlists', function () { | ||
53 | let servers: PeerTubeServer[] = [] | ||
54 | |||
55 | let playlistServer2Id1: number | ||
56 | let playlistServer2Id2: number | ||
57 | let playlistServer2UUID2: string | ||
58 | |||
59 | let playlistServer1Id: number | ||
60 | let playlistServer1DisplayName: string | ||
61 | let playlistServer1UUID: string | ||
62 | let playlistServer1UUID2: string | ||
63 | |||
64 | let playlistElementServer1Video4: number | ||
65 | let playlistElementServer1Video5: number | ||
66 | let playlistElementNSFW: number | ||
67 | |||
68 | let nsfwVideoServer1: number | ||
69 | |||
70 | let userTokenServer1: string | ||
71 | |||
72 | let commands: PlaylistsCommand[] | ||
73 | |||
74 | before(async function () { | ||
75 | this.timeout(240000) | ||
76 | |||
77 | servers = await createMultipleServers(3) | ||
78 | |||
79 | // Get the access tokens | ||
80 | await setAccessTokensToServers(servers) | ||
81 | await setDefaultVideoChannel(servers) | ||
82 | await setDefaultAccountAvatar(servers) | ||
83 | |||
84 | for (const server of servers) { | ||
85 | await server.config.disableTranscoding() | ||
86 | } | ||
87 | |||
88 | // Server 1 and server 2 follow each other | ||
89 | await doubleFollow(servers[0], servers[1]) | ||
90 | // Server 1 and server 3 follow each other | ||
91 | await doubleFollow(servers[0], servers[2]) | ||
92 | |||
93 | commands = servers.map(s => s.playlists) | ||
94 | |||
95 | { | ||
96 | servers[0].store.videos = [] | ||
97 | servers[1].store.videos = [] | ||
98 | servers[2].store.videos = [] | ||
99 | |||
100 | for (const server of servers) { | ||
101 | for (let i = 0; i < 7; i++) { | ||
102 | const name = `video ${i} server ${server.serverNumber}` | ||
103 | const video = await server.videos.upload({ attributes: { name, nsfw: false } }) | ||
104 | |||
105 | server.store.videos.push(video) | ||
106 | } | ||
107 | } | ||
108 | } | ||
109 | |||
110 | nsfwVideoServer1 = (await servers[0].videos.quickUpload({ name: 'NSFW video', nsfw: true })).id | ||
111 | |||
112 | userTokenServer1 = await servers[0].users.generateUserAndToken('user1') | ||
113 | |||
114 | await waitJobs(servers) | ||
115 | }) | ||
116 | |||
117 | describe('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 | }) | ||