From 16ccb43767c45e74877ab7beaa4adb61a404c128 Mon Sep 17 00:00:00 2001 From: Wicklow Date: Thu, 23 Feb 2023 15:39:09 +0100 Subject: [PATCH] Fix filters on playlists --- server/controllers/api/video-playlist.ts | 2 +- .../validators/videos/video-playlists.ts | 2 +- server/tests/api/videos/video-playlists.ts | 60 +++++++++++++++-- .../videos/playlists-command.ts | 6 +- support/doc/api/openapi.yaml | 67 ++++++++++++++++++- 5 files changed, 126 insertions(+), 11 deletions(-) diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index bcb60e265..08b0f971d 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts @@ -139,7 +139,7 @@ async function listVideoPlaylists (req: express.Request, res: express.Response) start: req.query.start, count: req.query.count, sort: req.query.sort, - type: req.query.type + type: req.query.playlistType }) return res.json(getFormattedObjects(resultList.data, resultList.total)) diff --git a/server/middlewares/validators/videos/video-playlists.ts b/server/middlewares/validators/videos/video-playlists.ts index e4b7e5c56..c631a16f8 100644 --- a/server/middlewares/validators/videos/video-playlists.ts +++ b/server/middlewares/validators/videos/video-playlists.ts @@ -142,7 +142,7 @@ const videoPlaylistsGetValidator = (fetchType: VideoPlaylistFetchType) => { const videoPlaylist = res.locals.videoPlaylistFull || res.locals.videoPlaylistSummary - // Video is unlisted, check we used the uuid to fetch it + // Playlist is unlisted, check we used the uuid to fetch it if (videoPlaylist.privacy === VideoPlaylistPrivacy.UNLISTED) { if (isUUIDValid(req.params.playlistId)) return next() diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index e8e653382..d9c5bdf16 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts @@ -114,7 +114,7 @@ describe('Test video playlists', function () { await waitJobs(servers) }) - describe('Get default playlists', function () { + describe('Check playlists filters and privacies', function () { it('Should list video playlist privacies', async function () { const privacies = await commands[0].getPrivacies() @@ -123,9 +123,21 @@ describe('Test video playlists', function () { expect(privacies[3]).to.equal('Private') }) - it('Should list watch later playlist', async function () { + it('Should filter on playlist type', async function () { + this.timeout(30000) + const token = servers[0].accessToken + await commands[0].create({ + attributes: { + displayName: 'my super playlist', + privacy: VideoPlaylistPrivacy.PUBLIC, + description: 'my super description', + thumbnailfile: 'thumbnail.jpg', + videoChannelId: servers[0].store.channel.id + } + }) + { const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.WATCH_LATER }) @@ -136,13 +148,51 @@ describe('Test video playlists', function () { expect(playlist.displayName).to.equal('Watch later') expect(playlist.type.id).to.equal(VideoPlaylistType.WATCH_LATER) expect(playlist.type.label).to.equal('Watch later') + expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PRIVATE) } { - const body = await commands[0].listByAccount({ token, handle: 'root', playlistType: VideoPlaylistType.REGULAR }) + const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.WATCH_LATER }) + const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.WATCH_LATER }) - expect(body.total).to.equal(0) - expect(body.data).to.have.lengthOf(0) + for (const body of [ bodyList, bodyChannel ]) { + expect(body.total).to.equal(0) + expect(body.data).to.have.lengthOf(0) + } + } + + { + const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.REGULAR }) + const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.REGULAR }) + + let playlist: VideoPlaylist = null + for (const body of [ bodyList, bodyChannel ]) { + + expect(body.total).to.equal(1) + expect(body.data).to.have.lengthOf(1) + + playlist = body.data[0] + expect(playlist.displayName).to.equal('my super playlist') + expect(playlist.privacy.id).to.equal(VideoPlaylistPrivacy.PUBLIC) + expect(playlist.type.id).to.equal(VideoPlaylistType.REGULAR) + } + + await commands[0].update({ + playlistId: playlist.id, + attributes: { + privacy: VideoPlaylistPrivacy.PRIVATE + } + }) + } + + { + const bodyList = await commands[0].list({ playlistType: VideoPlaylistType.REGULAR }) + const bodyChannel = await commands[0].listByChannel({ handle: 'root_channel', playlistType: VideoPlaylistType.REGULAR }) + + for (const body of [ bodyList, bodyChannel ]) { + expect(body.total).to.equal(0) + expect(body.data).to.have.lengthOf(0) + } } { diff --git a/shared/server-commands/videos/playlists-command.ts b/shared/server-commands/videos/playlists-command.ts index 516da0bf7..da3bef7b0 100644 --- a/shared/server-commands/videos/playlists-command.ts +++ b/shared/server-commands/videos/playlists-command.ts @@ -24,9 +24,10 @@ export class PlaylistsCommand extends AbstractCommand { start?: number count?: number sort?: string + playlistType?: VideoPlaylistType }) { const path = '/api/v1/video-playlists' - const query = pick(options, [ 'start', 'count', 'sort' ]) + const query = pick(options, [ 'start', 'count', 'sort', 'playlistType' ]) return this.getRequestBody>({ ...options, @@ -43,9 +44,10 @@ export class PlaylistsCommand extends AbstractCommand { start?: number count?: number sort?: string + playlistType?: VideoPlaylistType }) { const path = '/api/v1/video-channels/' + options.handle + '/video-playlists' - const query = pick(options, [ 'start', 'count', 'sort' ]) + const query = pick(options, [ 'start', 'count', 'sort', 'playlistType' ]) return this.getRequestBody>({ ...options, diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index fa50e8f17..d9e91c131 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -3801,6 +3801,34 @@ paths: schema: $ref: '#/components/schemas/VideoListResponse' + '/api/v1/video-channels/{channelHandle}/video-playlists': + get: + summary: List playlists of a channel + tags: + - Video Playlists + - Video Channels + parameters: + - $ref: '#/components/parameters/channelHandle' + - $ref: '#/components/parameters/start' + - $ref: '#/components/parameters/count' + - $ref: '#/components/parameters/sort' + - $ref: '#/components/parameters/videoPlaylistType' + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + total: + type: integer + example: 1 + data: + type: array + items: + $ref: '#/components/schemas/VideoPlaylist' + '/api/v1/video-channels/{channelHandle}/followers': get: tags: @@ -4045,6 +4073,7 @@ paths: - $ref: '#/components/parameters/start' - $ref: '#/components/parameters/count' - $ref: '#/components/parameters/sort' + - $ref: '#/components/parameters/videoPlaylistType' responses: '200': description: successful operation @@ -4361,7 +4390,35 @@ paths: format: seconds stopTimestamp: type: integer - format: seconds + + '/api/v1/accounts/{name}/video-playlists': + get: + summary: List playlists of an account + tags: + - Video Playlists + - Accounts + parameters: + - $ref: '#/components/parameters/name' + - $ref: '#/components/parameters/start' + - $ref: '#/components/parameters/count' + - $ref: '#/components/parameters/sort' + - $ref: '#/components/parameters/search' + - $ref: '#/components/parameters/videoPlaylistType' + responses: + '200': + description: successful operation + content: + application/json: + schema: + type: object + properties: + total: + type: integer + example: 1 + data: + type: array + items: + $ref: '#/components/schemas/VideoPlaylist' '/api/v1/accounts/{name}/video-channels': get: @@ -5920,7 +5977,13 @@ components: description: Ask the server to reinject videoFileToken in URLs in m3u8 playlist schema: type: boolean - + videoPlaylistType: + name: playlistType + in: query + required: false + schema: + $ref: '#/components/schemas/VideoPlaylistTypeSet' + securitySchemes: OAuth2: description: |