Introduce playlist command

pull/4271/head
Chocobozzz 2021-07-08 15:54:39 +02:00
parent 72cbfc5695
commit e6346d59e6
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
24 changed files with 839 additions and 1040 deletions

View File

@ -5,6 +5,7 @@ import { scheduleRefreshIfNeeded } from '@server/lib/activitypub/playlists'
import { Hooks } from '@server/lib/plugins/hooks' import { Hooks } from '@server/lib/plugins/hooks'
import { getServerActor } from '@server/models/application/application' import { getServerActor } from '@server/models/application/application'
import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models' import { MVideoPlaylistFull, MVideoPlaylistThumbnail, MVideoThumbnail } from '@server/types/models'
import { VideoPlaylistCreateResult, VideoPlaylistElementCreateResult } from '@shared/models'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model' import { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-create.model'
import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model' import { VideoPlaylistElementCreate } from '../../../shared/models/videos/playlist/video-playlist-element-create.model'
@ -202,7 +203,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) {
id: videoPlaylistCreated.id, id: videoPlaylistCreated.id,
shortUUID: uuidToShort(videoPlaylistCreated.uuid), shortUUID: uuidToShort(videoPlaylistCreated.uuid),
uuid: videoPlaylistCreated.uuid uuid: videoPlaylistCreated.uuid
} } as VideoPlaylistCreateResult
}) })
} }
@ -338,8 +339,8 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
return res.json({ return res.json({
videoPlaylistElement: { videoPlaylistElement: {
id: playlistElement.id id: playlistElement.id
} } as VideoPlaylistElementCreateResult
}).end() })
} }
async function updateVideoPlaylistElement (req: express.Request, res: express.Response) { async function updateVideoPlaylistElement (req: express.Request, res: express.Response) {

View File

@ -6,7 +6,6 @@ import { VideoPlaylistPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
cleanupTests, cleanupTests,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
makeActivityPubGetRequest, makeActivityPubGetRequest,
@ -74,9 +73,8 @@ describe('Test activitypub', function () {
} }
{ {
const playlistAttrs = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id } const attributes = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id }
const resCreate = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) playlist = await servers[0].playlistsCommand.create({ attributes })
playlist = resCreate.body.videoPlaylist
} }
await doubleFollow(servers[0], servers[1]) await doubleFollow(servers[0], servers[1])

View File

@ -5,12 +5,10 @@ import { HttpStatusCode } from '@shared/core-utils'
import { import {
cleanupTests, cleanupTests,
closeAllSequelize, closeAllSequelize,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
generateUserAccessToken, generateUserAccessToken,
getVideo, getVideo,
getVideoPlaylist,
killallServers, killallServers,
reRunServer, reRunServer,
ServerInfo, ServerInfo,
@ -58,15 +56,15 @@ describe('Test AP refresher', function () {
} }
{ {
const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } const attributes = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id }
const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) const created = await servers[1].playlistsCommand.create({ attributes })
playlistUUID1 = res.body.videoPlaylist.uuid playlistUUID1 = created.uuid
} }
{ {
const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id } const attributes = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id }
const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs }) const created = await servers[1].playlistsCommand.create({ attributes })
playlistUUID2 = res.body.videoPlaylist.uuid playlistUUID2 = created.uuid
} }
await doubleFollow(servers[0], servers[1]) await doubleFollow(servers[0], servers[1])
@ -144,13 +142,13 @@ describe('Test AP refresher', function () {
// Change UUID so the remote server returns a 404 // Change UUID so the remote server returns a 404
await setPlaylistField(servers[1].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e') await setPlaylistField(servers[1].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e')
await getVideoPlaylist(servers[0].url, playlistUUID1) await servers[0].playlistsCommand.get({ playlistId: playlistUUID1 })
await getVideoPlaylist(servers[0].url, playlistUUID2) await servers[0].playlistsCommand.get({ playlistId: playlistUUID2 })
await waitJobs(servers) await waitJobs(servers)
await getVideoPlaylist(servers[0].url, playlistUUID1, HttpStatusCode.OK_200) await servers[0].playlistsCommand.get({ playlistId: playlistUUID1, expectedStatus: HttpStatusCode.OK_200 })
await getVideoPlaylist(servers[0].url, playlistUUID2, HttpStatusCode.NOT_FOUND_404) await servers[0].playlistsCommand.get({ playlistId: playlistUUID2, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
}) })
}) })

View File

@ -1,19 +1,17 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha' import 'mocha'
import { VideoPlaylistPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
cleanupTests, cleanupTests,
flushAndRunServer, flushAndRunServer,
makeGetRequest, makeGetRequest,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
uploadVideo, setDefaultVideoChannel,
createVideoPlaylist, uploadVideo
setDefaultVideoChannel
} from '../../../../shared/extra-utils' } from '../../../../shared/extra-utils'
import { VideoPlaylistPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
describe('Test services API validators', function () { describe('Test services API validators', function () {
let server: ServerInfo let server: ServerInfo
@ -34,17 +32,15 @@ describe('Test services API validators', function () {
} }
{ {
const res = await createVideoPlaylist({ const created = await server.playlistsCommand.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'super playlist', displayName: 'super playlist',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
}) })
playlistUUID = res.body.videoPlaylist.uuid playlistUUID = created.uuid
} }
}) })

View File

@ -1,29 +1,29 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import 'mocha' import 'mocha'
import { VideoPlaylistCreateResult, VideoPlaylistPrivacy, VideoPlaylistType } from '@shared/models' import {
VideoPlaylistCreate,
VideoPlaylistCreateResult,
VideoPlaylistElementCreate,
VideoPlaylistElementUpdate,
VideoPlaylistPrivacy,
VideoPlaylistReorder,
VideoPlaylistType
} from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { import {
addVideoInPlaylist,
checkBadCountPagination, checkBadCountPagination,
checkBadSortPagination, checkBadSortPagination,
checkBadStartPagination, checkBadStartPagination,
cleanupTests, cleanupTests,
createVideoPlaylist,
deleteVideoPlaylist,
flushAndRunServer, flushAndRunServer,
generateUserAccessToken, generateUserAccessToken,
getAccountPlaylistsListWithToken,
getVideoPlaylist,
immutableAssign, immutableAssign,
makeGetRequest, makeGetRequest,
removeVideoFromPlaylist, PlaylistsCommand,
reorderVideosPlaylist,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
setDefaultVideoChannel, setDefaultVideoChannel,
updateVideoPlaylist,
updateVideoPlaylistElement,
uploadVideoAndGetId uploadVideoAndGetId
} from '../../../../shared/extra-utils' } from '../../../../shared/extra-utils'
@ -36,7 +36,9 @@ describe('Test video playlists API validator', function () {
let watchLaterPlaylistId: number let watchLaterPlaylistId: number
let videoId: number let videoId: number
let playlistElementId: number let elementId: number
let command: PlaylistsCommand
// --------------------------------------------------------------- // ---------------------------------------------------------------
@ -51,34 +53,37 @@ describe('Test video playlists API validator', function () {
userAccessToken = await generateUserAccessToken(server, 'user1') userAccessToken = await generateUserAccessToken(server, 'user1')
videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id videoId = (await uploadVideoAndGetId({ server, videoName: 'video 1' })).id
command = server.playlistsCommand
{ {
const res = await getAccountPlaylistsListWithToken(server.url, server.accessToken, 'root', 0, 5, VideoPlaylistType.WATCH_LATER) const { data } = await command.listByAccount({
watchLaterPlaylistId = res.body.data[0].id token: server.accessToken,
handle: 'root',
start: 0,
count: 5,
playlistType: VideoPlaylistType.WATCH_LATER
})
watchLaterPlaylistId = data[0].id
} }
{ {
const res = await createVideoPlaylist({ playlist = await command.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'super playlist', displayName: 'super playlist',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
}) })
playlist = res.body.videoPlaylist
} }
{ {
const res = await createVideoPlaylist({ const created = await command.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'private', displayName: 'private',
privacy: VideoPlaylistPrivacy.PRIVATE privacy: VideoPlaylistPrivacy.PRIVATE
} }
}) })
privatePlaylistUUID = res.body.videoPlaylist.uuid privatePlaylistUUID = created.uuid
} }
}) })
@ -163,47 +168,50 @@ describe('Test video playlists API validator', function () {
describe('When getting a video playlist', function () { describe('When getting a video playlist', function () {
it('Should fail with a bad id or uuid', async function () { it('Should fail with a bad id or uuid', async function () {
await getVideoPlaylist(server.url, 'toto', HttpStatusCode.BAD_REQUEST_400) await command.get({ playlistId: 'toto', expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
}) })
it('Should fail with an unknown playlist', async function () { it('Should fail with an unknown playlist', async function () {
await getVideoPlaylist(server.url, 42, HttpStatusCode.NOT_FOUND_404) await command.get({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
}) })
it('Should fail to get an unlisted playlist with the number id', async function () { it('Should fail to get an unlisted playlist with the number id', async function () {
const res = await createVideoPlaylist({ const playlist = await command.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'super playlist', displayName: 'super playlist',
videoChannelId: server.videoChannel.id, videoChannelId: server.videoChannel.id,
privacy: VideoPlaylistPrivacy.UNLISTED privacy: VideoPlaylistPrivacy.UNLISTED
} }
}) })
const playlist = res.body.videoPlaylist
await getVideoPlaylist(server.url, playlist.id, HttpStatusCode.NOT_FOUND_404) await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200) await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200) await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
}) })
}) })
describe('When creating/updating a video playlist', function () { describe('When creating/updating a video playlist', function () {
const getBase = (playlistAttrs: any = {}, wrapper: any = {}) => { const getBase = (
return Object.assign({ attributes?: Partial<VideoPlaylistCreate>,
expectedStatus: HttpStatusCode.BAD_REQUEST_400, wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
url: server.url, ) => {
token: server.accessToken, return {
playlistAttrs: Object.assign({ attributes: {
displayName: 'display name', displayName: 'display name',
privacy: VideoPlaylistPrivacy.UNLISTED, privacy: VideoPlaylistPrivacy.UNLISTED,
thumbnailfile: 'thumbnail.jpg', thumbnailfile: 'thumbnail.jpg',
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id,
}, playlistAttrs)
}, wrapper) ...attributes
},
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
...wrapper
}
} }
const getUpdate = (params: any, playlistId: number | string) => { const getUpdate = (params: any, playlistId: number | string) => {
return immutableAssign(params, { playlistId: playlistId }) return immutableAssign(params, { playlistId: playlistId })
@ -212,86 +220,86 @@ describe('Test video playlists API validator', function () {
it('Should fail with an unauthenticated user', async function () { it('Should fail with an unauthenticated user', async function () {
const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail without displayName', async function () { it('Should fail without displayName', async function () {
const params = getBase({ displayName: undefined }) const params = getBase({ displayName: undefined })
await createVideoPlaylist(params) await command.create(params)
}) })
it('Should fail with an incorrect display name', async function () { it('Should fail with an incorrect display name', async function () {
const params = getBase({ displayName: 's'.repeat(300) }) const params = getBase({ displayName: 's'.repeat(300) })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an incorrect description', async function () { it('Should fail with an incorrect description', async function () {
const params = getBase({ description: 't' }) const params = getBase({ description: 't' })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an incorrect privacy', async function () { it('Should fail with an incorrect privacy', async function () {
const params = getBase({ privacy: 45 }) const params = getBase({ privacy: 45 })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an unknown video channel id', async function () { it('Should fail with an unknown video channel id', async function () {
const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with an incorrect thumbnail file', async function () { it('Should fail with an incorrect thumbnail file', async function () {
const params = getBase({ thumbnailfile: 'video_short.mp4' }) const params = getBase({ thumbnailfile: 'video_short.mp4' })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail with a thumbnail file too big', async function () { it('Should fail with a thumbnail file too big', async function () {
const params = getBase({ thumbnailfile: 'preview-big.png' }) const params = getBase({ thumbnailfile: 'preview-big.png' })
await createVideoPlaylist(params) await command.create(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
}) })
it('Should fail to set "public" a playlist not assigned to a channel', async function () { it('Should fail to set "public" a playlist not assigned to a channel', async function () {
const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined }) const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined })
const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' }) const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' as any })
const params3 = getBase({ privacy: undefined, videoChannelId: 'null' }) const params3 = getBase({ privacy: undefined, videoChannelId: 'null' as any })
await createVideoPlaylist(params) await command.create(params)
await createVideoPlaylist(params2) await command.create(params2)
await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID)) await command.update(getUpdate(params, privatePlaylistUUID))
await updateVideoPlaylist(getUpdate(params2, playlist.shortUUID)) await command.update(getUpdate(params2, playlist.shortUUID))
await updateVideoPlaylist(getUpdate(params3, playlist.shortUUID)) await command.update(getUpdate(params3, playlist.shortUUID))
}) })
it('Should fail with an unknown playlist to update', async function () { it('Should fail with an unknown playlist to update', async function () {
await updateVideoPlaylist(getUpdate( await command.update(getUpdate(
getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }), getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
42 42
)) ))
}) })
it('Should fail to update a playlist of another user', async function () { it('Should fail to update a playlist of another user', async function () {
await updateVideoPlaylist(getUpdate( await command.update(getUpdate(
getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }), getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }),
playlist.shortUUID playlist.shortUUID
)) ))
}) })
it('Should fail to update the watch later playlist', async function () { it('Should fail to update the watch later playlist', async function () {
await updateVideoPlaylist(getUpdate( await command.update(getUpdate(
getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }), getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
watchLaterPlaylistId watchLaterPlaylistId
)) ))
@ -300,146 +308,158 @@ describe('Test video playlists API validator', function () {
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
{ {
const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 }) const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
await createVideoPlaylist(params) await command.create(params)
} }
{ {
const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID)) await command.update(getUpdate(params, playlist.shortUUID))
} }
}) })
}) })
describe('When adding an element in a playlist', function () { describe('When adding an element in a playlist', function () {
const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { const getBase = (
return Object.assign({ attributes?: Partial<VideoPlaylistElementCreate>,
expectedStatus: HttpStatusCode.BAD_REQUEST_400, wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
url: server.url, ) => {
token: server.accessToken, return {
playlistId: playlist.id, attributes: {
elementAttrs: Object.assign({
videoId, videoId,
startTimestamp: 2, startTimestamp: 2,
stopTimestamp: 3 stopTimestamp: 3,
}, elementAttrs)
}, wrapper) ...attributes
},
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
playlistId: playlist.id,
...wrapper
}
} }
it('Should fail with an unauthenticated user', async function () { it('Should fail with an unauthenticated user', async function () {
const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await addVideoInPlaylist(params) await command.addElement(params)
}) })
it('Should fail with the playlist of another user', async function () { it('Should fail with the playlist of another user', async function () {
const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
await addVideoInPlaylist(params) await command.addElement(params)
}) })
it('Should fail with an unknown or incorrect playlist id', async function () { it('Should fail with an unknown or incorrect playlist id', async function () {
{ {
const params = getBase({}, { playlistId: 'toto' }) const params = getBase({}, { playlistId: 'toto' })
await addVideoInPlaylist(params) await command.addElement(params)
} }
{ {
const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await addVideoInPlaylist(params) await command.addElement(params)
} }
}) })
it('Should fail with an unknown or incorrect video id', async function () { it('Should fail with an unknown or incorrect video id', async function () {
const params = getBase({ videoId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({ videoId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await addVideoInPlaylist(params) await command.addElement(params)
}) })
it('Should fail with a bad start/stop timestamp', async function () { it('Should fail with a bad start/stop timestamp', async function () {
{ {
const params = getBase({ startTimestamp: -42 }) const params = getBase({ startTimestamp: -42 })
await addVideoInPlaylist(params) await command.addElement(params)
} }
{ {
const params = getBase({ stopTimestamp: 'toto' as any }) const params = getBase({ stopTimestamp: 'toto' as any })
await addVideoInPlaylist(params) await command.addElement(params)
} }
}) })
it('Succeed with the correct params', async function () { it('Succeed with the correct params', async function () {
const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 }) const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
const res = await addVideoInPlaylist(params) const created = await command.addElement(params)
playlistElementId = res.body.videoPlaylistElement.id elementId = created.id
}) })
}) })
describe('When updating an element in a playlist', function () { describe('When updating an element in a playlist', function () {
const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { const getBase = (
return Object.assign({ attributes?: Partial<VideoPlaylistElementUpdate>,
url: server.url, wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
token: server.accessToken, ) => {
elementAttrs: Object.assign({ return {
attributes: {
startTimestamp: 1, startTimestamp: 1,
stopTimestamp: 2 stopTimestamp: 2,
}, elementAttrs),
playlistElementId, ...attributes
},
elementId,
playlistId: playlist.id, playlistId: playlist.id,
expectedStatus: HttpStatusCode.BAD_REQUEST_400 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
}, wrapper)
...wrapper
}
} }
it('Should fail with an unauthenticated user', async function () { it('Should fail with an unauthenticated user', async function () {
const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
}) })
it('Should fail with the playlist of another user', async function () { it('Should fail with the playlist of another user', async function () {
const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
}) })
it('Should fail with an unknown or incorrect playlist id', async function () { it('Should fail with an unknown or incorrect playlist id', async function () {
{ {
const params = getBase({}, { playlistId: 'toto' }) const params = getBase({}, { playlistId: 'toto' })
await updateVideoPlaylistElement(params) await command.updateElement(params)
} }
{ {
const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
} }
}) })
it('Should fail with an unknown or incorrect playlistElement id', async function () { it('Should fail with an unknown or incorrect playlistElement id', async function () {
{ {
const params = getBase({}, { playlistElementId: 'toto' }) const params = getBase({}, { elementId: 'toto' })
await updateVideoPlaylistElement(params) await command.updateElement(params)
} }
{ {
const params = getBase({}, { playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
} }
}) })
it('Should fail with a bad start/stop timestamp', async function () { it('Should fail with a bad start/stop timestamp', async function () {
{ {
const params = getBase({ startTimestamp: 'toto' as any }) const params = getBase({ startTimestamp: 'toto' as any })
await updateVideoPlaylistElement(params) await command.updateElement(params)
} }
{ {
const params = getBase({ stopTimestamp: -42 }) const params = getBase({ stopTimestamp: -42 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
} }
}) })
it('Should fail with an unknown element', async function () { it('Should fail with an unknown element', async function () {
const params = getBase({}, { playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({}, { elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
}) })
it('Succeed with the correct params', async function () { it('Succeed with the correct params', async function () {
const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
await updateVideoPlaylistElement(params) await command.updateElement(params)
}) })
}) })
@ -447,18 +467,24 @@ describe('Test video playlists API validator', function () {
let videoId3: number let videoId3: number
let videoId4: number let videoId4: number
const getBase = (elementAttrs: any = {}, wrapper: any = {}) => { const getBase = (
return Object.assign({ attributes?: Partial<VideoPlaylistReorder>,
url: server.url, wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
token: server.accessToken, ) => {
playlistId: playlist.shortUUID, return {
elementAttrs: Object.assign({ attributes: {
startPosition: 1, startPosition: 1,
insertAfterPosition: 2, insertAfterPosition: 2,
reorderLength: 3 reorderLength: 3,
}, elementAttrs),
expectedStatus: HttpStatusCode.BAD_REQUEST_400 ...attributes
}, wrapper) },
playlistId: playlist.shortUUID,
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
...wrapper
}
} }
before(async function () { before(async function () {
@ -466,91 +492,86 @@ describe('Test video playlists API validator', function () {
videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id
for (const id of [ videoId3, videoId4 ]) { for (const id of [ videoId3, videoId4 ]) {
await addVideoInPlaylist({ await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
url: server.url,
token: server.accessToken,
playlistId: playlist.shortUUID,
elementAttrs: { videoId: id }
})
} }
}) })
it('Should fail with an unauthenticated user', async function () { it('Should fail with an unauthenticated user', async function () {
const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
}) })
it('Should fail with the playlist of another user', async function () { it('Should fail with the playlist of another user', async function () {
const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) const params = getBase({}, { token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
}) })
it('Should fail with an invalid playlist', async function () { it('Should fail with an invalid playlist', async function () {
{ {
const params = getBase({}, { playlistId: 'toto' }) const params = getBase({}, { playlistId: 'toto' })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({}, { playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
}) })
it('Should fail with an invalid start position', async function () { it('Should fail with an invalid start position', async function () {
{ {
const params = getBase({ startPosition: -1 }) const params = getBase({ startPosition: -1 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({ startPosition: 'toto' as any }) const params = getBase({ startPosition: 'toto' as any })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({ startPosition: 42 }) const params = getBase({ startPosition: 42 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
}) })
it('Should fail with an invalid insert after position', async function () { it('Should fail with an invalid insert after position', async function () {
{ {
const params = getBase({ insertAfterPosition: 'toto' as any }) const params = getBase({ insertAfterPosition: 'toto' as any })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({ insertAfterPosition: -2 }) const params = getBase({ insertAfterPosition: -2 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({ insertAfterPosition: 42 }) const params = getBase({ insertAfterPosition: 42 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
}) })
it('Should fail with an invalid reorder length', async function () { it('Should fail with an invalid reorder length', async function () {
{ {
const params = getBase({ reorderLength: 'toto' as any }) const params = getBase({ reorderLength: 'toto' as any })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({ reorderLength: -2 }) const params = getBase({ reorderLength: -2 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
{ {
const params = getBase({ reorderLength: 42 }) const params = getBase({ reorderLength: 42 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
} }
}) })
it('Succeed with the correct params', async function () { it('Succeed with the correct params', async function () {
const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 }) const params = getBase({}, { expectedStatus: HttpStatusCode.NO_CONTENT_204 })
await reorderVideosPlaylist(params) await command.reorderElements(params)
}) })
}) })
@ -601,76 +622,76 @@ describe('Test video playlists API validator', function () {
}) })
describe('When deleting an element in a playlist', function () { describe('When deleting an element in a playlist', function () {
const getBase = (wrapper: any = {}) => { const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
return Object.assign({ return {
url: server.url, elementId,
token: server.accessToken,
playlistElementId,
playlistId: playlist.uuid, playlistId: playlist.uuid,
expectedStatus: HttpStatusCode.BAD_REQUEST_400 expectedStatus: HttpStatusCode.BAD_REQUEST_400,
}, wrapper)
...wrapper
}
} }
it('Should fail with an unauthenticated user', async function () { it('Should fail with an unauthenticated user', async function () {
const params = getBase({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 }) const params = getBase({ token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await removeVideoFromPlaylist(params) await command.removeElement(params)
}) })
it('Should fail with the playlist of another user', async function () { it('Should fail with the playlist of another user', async function () {
const params = getBase({ token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 }) const params = getBase({ token: userAccessToken, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
await removeVideoFromPlaylist(params) await command.removeElement(params)
}) })
it('Should fail with an unknown or incorrect playlist id', async function () { it('Should fail with an unknown or incorrect playlist id', async function () {
{ {
const params = getBase({ playlistId: 'toto' }) const params = getBase({ playlistId: 'toto' })
await removeVideoFromPlaylist(params) await command.removeElement(params)
} }
{ {
const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await removeVideoFromPlaylist(params) await command.removeElement(params)
} }
}) })
it('Should fail with an unknown or incorrect video id', async function () { it('Should fail with an unknown or incorrect video id', async function () {
{ {
const params = getBase({ playlistElementId: 'toto' }) const params = getBase({ elementId: 'toto' as any })
await removeVideoFromPlaylist(params) await command.removeElement(params)
} }
{ {
const params = getBase({ playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await removeVideoFromPlaylist(params) await command.removeElement(params)
} }
}) })
it('Should fail with an unknown element', async function () { it('Should fail with an unknown element', async function () {
const params = getBase({ playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 }) const params = getBase({ elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await removeVideoFromPlaylist(params) await command.removeElement(params)
}) })
it('Succeed with the correct params', async function () { it('Succeed with the correct params', async function () {
const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 }) const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
await removeVideoFromPlaylist(params) await command.removeElement(params)
}) })
}) })
describe('When deleting a playlist', function () { describe('When deleting a playlist', function () {
it('Should fail with an unknown playlist', async function () { it('Should fail with an unknown playlist', async function () {
await deleteVideoPlaylist(server.url, server.accessToken, 42, HttpStatusCode.NOT_FOUND_404) await command.delete({ playlistId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
}) })
it('Should fail with a playlist of another user', async function () { it('Should fail with a playlist of another user', async function () {
await deleteVideoPlaylist(server.url, userAccessToken, playlist.uuid, HttpStatusCode.FORBIDDEN_403) await command.delete({ token: userAccessToken, playlistId: playlist.uuid, expectedStatus: HttpStatusCode.FORBIDDEN_403 })
}) })
it('Should fail with the watch later playlist', async function () { it('Should fail with the watch later playlist', async function () {
await deleteVideoPlaylist(server.url, server.accessToken, watchLaterPlaylistId, HttpStatusCode.BAD_REQUEST_400) await command.delete({ playlistId: watchLaterPlaylistId, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
}) })
it('Should succeed with the correct params', async function () { it('Should succeed with the correct params', async function () {
await deleteVideoPlaylist(server.url, server.accessToken, playlist.uuid) await command.delete({ playlistId: playlist.uuid })
}) })
}) })

View File

@ -3,12 +3,8 @@
import 'mocha' import 'mocha'
import * as chai from 'chai' import * as chai from 'chai'
import { import {
addVideoInPlaylist,
cleanupTests, cleanupTests,
createVideoPlaylist,
deleteVideoPlaylist,
flushAndRunMultipleServers, flushAndRunMultipleServers,
getVideoPlaylistsList,
SearchCommand, SearchCommand,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
@ -46,16 +42,11 @@ describe('Test ActivityPub playlists search', function () {
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: servers[0].videoChannel.id videoChannelId: servers[0].videoChannel.id
} }
const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs: attributes }) const created = await servers[0].playlistsCommand.create({ attributes })
playlistServer1UUID = res.body.videoPlaylist.uuid playlistServer1UUID = created.uuid
for (const videoId of [ video1, video2 ]) { for (const videoId of [ video1, video2 ]) {
await addVideoInPlaylist({ await servers[0].playlistsCommand.addElement({ playlistId: playlistServer1UUID, attributes: { videoId } })
url: servers[0].url,
token: servers[0].accessToken,
playlistId: playlistServer1UUID,
elementAttrs: { videoId }
})
} }
} }
@ -68,15 +59,10 @@ describe('Test ActivityPub playlists search', function () {
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: servers[1].videoChannel.id videoChannelId: servers[1].videoChannel.id
} }
const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs: attributes }) const created = await servers[1].playlistsCommand.create({ attributes })
playlistServer2UUID = res.body.videoPlaylist.uuid playlistServer2UUID = created.uuid
await addVideoInPlaylist({ await servers[1].playlistsCommand.addElement({ playlistId: playlistServer2UUID, attributes: { videoId } })
url: servers[1].url,
token: servers[1].accessToken,
playlistId: playlistServer2UUID,
elementAttrs: { videoId }
})
} }
await waitJobs(servers) await waitJobs(servers)
@ -154,21 +140,16 @@ describe('Test ActivityPub playlists search', function () {
}) })
it('Should not list this remote playlist', async function () { it('Should not list this remote playlist', async function () {
const res = await getVideoPlaylistsList(servers[0].url, 0, 10) const body = await servers[0].playlistsCommand.list({ start: 0, count: 10 })
expect(res.body.total).to.equal(1) expect(body.total).to.equal(1)
expect(res.body.data).to.have.lengthOf(1) expect(body.data).to.have.lengthOf(1)
expect(res.body.data[0].displayName).to.equal('playlist 1 on server 1') expect(body.data[0].displayName).to.equal('playlist 1 on server 1')
}) })
it('Should update the playlist of server 2, and refresh it on server 1', async function () { it('Should update the playlist of server 2, and refresh it on server 1', async function () {
this.timeout(60000) this.timeout(60000)
await addVideoInPlaylist({ await servers[1].playlistsCommand.addElement({ playlistId: playlistServer2UUID, attributes: { videoId: video2Server2 } })
url: servers[1].url,
token: servers[1].accessToken,
playlistId: playlistServer2UUID,
elementAttrs: { videoId: video2Server2 }
})
await waitJobs(servers) await waitJobs(servers)
// Expire playlist // Expire playlist
@ -192,7 +173,7 @@ describe('Test ActivityPub playlists search', function () {
it('Should delete playlist of server 2, and delete it on server 1', async function () { it('Should delete playlist of server 2, and delete it on server 1', async function () {
this.timeout(60000) this.timeout(60000)
await deleteVideoPlaylist(servers[1].url, servers[1].accessToken, playlistServer2UUID) await servers[1].playlistsCommand.delete({ playlistId: playlistServer2UUID })
await waitJobs(servers) await waitJobs(servers)
// Expiration // Expiration

View File

@ -4,9 +4,7 @@ import 'mocha'
import * as chai from 'chai' import * as chai from 'chai'
import { VideoPlaylistPrivacy } from '@shared/models' import { VideoPlaylistPrivacy } from '@shared/models'
import { import {
addVideoInPlaylist,
cleanupTests, cleanupTests,
createVideoPlaylist,
flushAndRunServer, flushAndRunServer,
SearchCommand, SearchCommand,
ServerInfo, ServerInfo,
@ -37,14 +35,9 @@ describe('Test playlists search', function () {
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) const created = await server.playlistsCommand.create({ attributes })
await addVideoInPlaylist({ await server.playlistsCommand.addElement({ playlistId: created.id, attributes: { videoId } })
url: server.url,
token: server.accessToken,
playlistId: res.body.videoPlaylist.id,
elementAttrs: { videoId }
})
} }
{ {
@ -53,14 +46,9 @@ describe('Test playlists search', function () {
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
const res = await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) const created = await server.playlistsCommand.create({ attributes })
await addVideoInPlaylist({ await server.playlistsCommand.addElement({ playlistId: created.id, attributes: { videoId } })
url: server.url,
token: server.accessToken,
playlistId: res.body.videoPlaylist.id,
elementAttrs: { videoId }
})
} }
{ {
@ -69,7 +57,7 @@ describe('Test playlists search', function () {
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
await createVideoPlaylist({ url: server.url, token: server.accessToken, playlistAttrs: attributes }) await server.playlistsCommand.create({ attributes })
} }
command = server.searchCommand command = server.searchCommand

View File

@ -4,8 +4,6 @@ import 'mocha'
import * as chai from 'chai' import * as chai from 'chai'
import { Video, VideoPlaylistPrivacy } from '@shared/models' import { Video, VideoPlaylistPrivacy } from '@shared/models'
import { import {
addVideoInPlaylist,
createVideoPlaylist,
getVideosList, getVideosList,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
@ -41,24 +39,20 @@ describe('Test services', function () {
} }
{ {
const res = await createVideoPlaylist({ const created = await server.playlistsCommand.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'The Life and Times of Scrooge McDuck', displayName: 'The Life and Times of Scrooge McDuck',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id videoChannelId: server.videoChannel.id
} }
}) })
playlistUUID = res.body.videoPlaylist.uuid playlistUUID = created.uuid
playlistDisplayName = 'The Life and Times of Scrooge McDuck' playlistDisplayName = 'The Life and Times of Scrooge McDuck'
await addVideoInPlaylist({ await server.playlistsCommand.addElement({
url: server.url, playlistId: created.id,
token: server.accessToken, attributes: {
playlistId: res.body.videoPlaylist.id,
elementAttrs: {
videoId: video.id videoId: video.id
} }
}) })

View File

@ -7,7 +7,6 @@ import {
addVideoCommentThread, addVideoCommentThread,
cleanupTests, cleanupTests,
createUser, createUser,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
ServerInfo, ServerInfo,
@ -178,10 +177,8 @@ describe('Test stats (excluding redundancy)', function () {
} }
{ {
await createVideoPlaylist({ await server.playlistsCommand.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'playlist for count', displayName: 'playlist for count',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: channelId videoChannelId: channelId

View File

@ -1,16 +1,11 @@
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
import * as chai from 'chai'
import 'mocha' import 'mocha'
import * as chai from 'chai'
import { import {
addVideoInPlaylist,
cleanupTests, cleanupTests,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
getVideoPlaylistsList,
removeVideoFromPlaylist,
reorderVideosPlaylist,
ServerInfo, ServerInfo,
setAccessTokensToServers, setAccessTokensToServers,
setDefaultVideoChannel, setDefaultVideoChannel,
@ -25,8 +20,8 @@ const expect = chai.expect
describe('Playlist thumbnail', function () { describe('Playlist thumbnail', function () {
let servers: ServerInfo[] = [] let servers: ServerInfo[] = []
let playlistWithoutThumbnail: number let playlistWithoutThumbnailId: number
let playlistWithThumbnail: number let playlistWithThumbnailId: number
let withThumbnailE1: number let withThumbnailE1: number
let withThumbnailE2: number let withThumbnailE2: number
@ -37,15 +32,15 @@ describe('Playlist thumbnail', function () {
let video2: number let video2: number
async function getPlaylistWithoutThumbnail (server: ServerInfo) { async function getPlaylistWithoutThumbnail (server: ServerInfo) {
const res = await getVideoPlaylistsList(server.url, 0, 10) const body = await server.playlistsCommand.list({ start: 0, count: 10 })
return res.body.data.find(p => p.displayName === 'playlist without thumbnail') return body.data.find(p => p.displayName === 'playlist without thumbnail')
} }
async function getPlaylistWithThumbnail (server: ServerInfo) { async function getPlaylistWithThumbnail (server: ServerInfo) {
const res = await getVideoPlaylistsList(server.url, 0, 10) const body = await server.playlistsCommand.list({ start: 0, count: 10 })
return res.body.data.find(p => p.displayName === 'playlist with thumbnail') return body.data.find(p => p.displayName === 'playlist with thumbnail')
} }
before(async function () { before(async function () {
@ -69,24 +64,20 @@ describe('Playlist thumbnail', function () {
it('Should automatically update the thumbnail when adding an element', async function () { it('Should automatically update the thumbnail when adding an element', async function () {
this.timeout(30000) this.timeout(30000)
const res = await createVideoPlaylist({ const created = await servers[1].playlistsCommand.create({
url: servers[1].url, attributes: {
token: servers[1].accessToken,
playlistAttrs: {
displayName: 'playlist without thumbnail', displayName: 'playlist without thumbnail',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: servers[1].videoChannel.id videoChannelId: servers[1].videoChannel.id
} }
}) })
playlistWithoutThumbnail = res.body.videoPlaylist.id playlistWithoutThumbnailId = created.id
const res2 = await addVideoInPlaylist({ const added = await servers[1].playlistsCommand.addElement({
url: servers[1].url, playlistId: playlistWithoutThumbnailId,
token: servers[1].accessToken, attributes: { videoId: video1 }
playlistId: playlistWithoutThumbnail,
elementAttrs: { videoId: video1 }
}) })
withoutThumbnailE1 = res2.body.videoPlaylistElement.id withoutThumbnailE1 = added.id
await waitJobs(servers) await waitJobs(servers)
@ -99,25 +90,21 @@ describe('Playlist thumbnail', function () {
it('Should not update the thumbnail if we explicitly uploaded a thumbnail', async function () { it('Should not update the thumbnail if we explicitly uploaded a thumbnail', async function () {
this.timeout(30000) this.timeout(30000)
const res = await createVideoPlaylist({ const created = await servers[1].playlistsCommand.create({
url: servers[1].url, attributes: {
token: servers[1].accessToken,
playlistAttrs: {
displayName: 'playlist with thumbnail', displayName: 'playlist with thumbnail',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: servers[1].videoChannel.id, videoChannelId: servers[1].videoChannel.id,
thumbnailfile: 'thumbnail.jpg' thumbnailfile: 'thumbnail.jpg'
} }
}) })
playlistWithThumbnail = res.body.videoPlaylist.id playlistWithThumbnailId = created.id
const res2 = await addVideoInPlaylist({ const added = await servers[1].playlistsCommand.addElement({
url: servers[1].url, playlistId: playlistWithThumbnailId,
token: servers[1].accessToken, attributes: { videoId: video1 }
playlistId: playlistWithThumbnail,
elementAttrs: { videoId: video1 }
}) })
withThumbnailE1 = res2.body.videoPlaylistElement.id withThumbnailE1 = added.id
await waitJobs(servers) await waitJobs(servers)
@ -130,19 +117,15 @@ describe('Playlist thumbnail', function () {
it('Should automatically update the thumbnail when moving the first element', async function () { it('Should automatically update the thumbnail when moving the first element', async function () {
this.timeout(30000) this.timeout(30000)
const res = await addVideoInPlaylist({ const added = await servers[1].playlistsCommand.addElement({
url: servers[1].url, playlistId: playlistWithoutThumbnailId,
token: servers[1].accessToken, attributes: { videoId: video2 }
playlistId: playlistWithoutThumbnail,
elementAttrs: { videoId: video2 }
}) })
withoutThumbnailE2 = res.body.videoPlaylistElement.id withoutThumbnailE2 = added.id
await reorderVideosPlaylist({ await servers[1].playlistsCommand.reorderElements({
url: servers[1].url, playlistId: playlistWithoutThumbnailId,
token: servers[1].accessToken, attributes: {
playlistId: playlistWithoutThumbnail,
elementAttrs: {
startPosition: 1, startPosition: 1,
insertAfterPosition: 2 insertAfterPosition: 2
} }
@ -159,19 +142,15 @@ describe('Playlist thumbnail', function () {
it('Should not update the thumbnail when moving the first element if we explicitly uploaded a thumbnail', async function () { it('Should not update the thumbnail when moving the first element if we explicitly uploaded a thumbnail', async function () {
this.timeout(30000) this.timeout(30000)
const res = await addVideoInPlaylist({ const added = await servers[1].playlistsCommand.addElement({
url: servers[1].url, playlistId: playlistWithThumbnailId,
token: servers[1].accessToken, attributes: { videoId: video2 }
playlistId: playlistWithThumbnail,
elementAttrs: { videoId: video2 }
}) })
withThumbnailE2 = res.body.videoPlaylistElement.id withThumbnailE2 = added.id
await reorderVideosPlaylist({ await servers[1].playlistsCommand.reorderElements({
url: servers[1].url, playlistId: playlistWithThumbnailId,
token: servers[1].accessToken, attributes: {
playlistId: playlistWithThumbnail,
elementAttrs: {
startPosition: 1, startPosition: 1,
insertAfterPosition: 2 insertAfterPosition: 2
} }
@ -188,11 +167,9 @@ describe('Playlist thumbnail', function () {
it('Should automatically update the thumbnail when deleting the first element', async function () { it('Should automatically update the thumbnail when deleting the first element', async function () {
this.timeout(30000) this.timeout(30000)
await removeVideoFromPlaylist({ await servers[1].playlistsCommand.removeElement({
url: servers[1].url, playlistId: playlistWithoutThumbnailId,
token: servers[1].accessToken, elementId: withoutThumbnailE1
playlistId: playlistWithoutThumbnail,
playlistElementId: withoutThumbnailE1
}) })
await waitJobs(servers) await waitJobs(servers)
@ -206,11 +183,9 @@ describe('Playlist thumbnail', function () {
it('Should not update the thumbnail when deleting the first element if we explicitly uploaded a thumbnail', async function () { it('Should not update the thumbnail when deleting the first element if we explicitly uploaded a thumbnail', async function () {
this.timeout(30000) this.timeout(30000)
await removeVideoFromPlaylist({ await servers[1].playlistsCommand.removeElement({
url: servers[1].url, playlistId: playlistWithThumbnailId,
token: servers[1].accessToken, elementId: withThumbnailE1
playlistId: playlistWithThumbnail,
playlistElementId: withThumbnailE1
}) })
await waitJobs(servers) await waitJobs(servers)
@ -224,11 +199,9 @@ describe('Playlist thumbnail', function () {
it('Should the thumbnail when we delete the last element', async function () { it('Should the thumbnail when we delete the last element', async function () {
this.timeout(30000) this.timeout(30000)
await removeVideoFromPlaylist({ await servers[1].playlistsCommand.removeElement({
url: servers[1].url, playlistId: playlistWithoutThumbnailId,
token: servers[1].accessToken, elementId: withoutThumbnailE2
playlistId: playlistWithoutThumbnail,
playlistElementId: withoutThumbnailE2
}) })
await waitJobs(servers) await waitJobs(servers)
@ -242,11 +215,9 @@ describe('Playlist thumbnail', function () {
it('Should not update the thumbnail when we delete the last element if we explicitly uploaded a thumbnail', async function () { it('Should not update the thumbnail when we delete the last element if we explicitly uploaded a thumbnail', async function () {
this.timeout(30000) this.timeout(30000)
await removeVideoFromPlaylist({ await servers[1].playlistsCommand.removeElement({
url: servers[1].url, playlistId: playlistWithThumbnailId,
token: servers[1].accessToken, elementId: withThumbnailE2
playlistId: playlistWithThumbnail,
playlistElementId: withThumbnailE2
}) })
await waitJobs(servers) await waitJobs(servers)

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@ import {
buildServerDirectory, buildServerDirectory,
cleanupTests, cleanupTests,
CLICommand, CLICommand,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
killallServers, killallServers,
@ -77,10 +76,8 @@ describe('Test prune storage scripts', function () {
await updateMyAvatar({ url: server.url, accessToken: server.accessToken, fixture: 'avatar.png' }) await updateMyAvatar({ url: server.url, accessToken: server.accessToken, fixture: 'avatar.png' })
await createVideoPlaylist({ await server.playlistsCommand.create({
url: server.url, attributes: {
token: server.accessToken,
playlistAttrs: {
displayName: 'playlist', displayName: 'playlist',
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id, videoChannelId: server.videoChannel.id,

View File

@ -6,9 +6,7 @@ import { omit } from 'lodash'
import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes' import { HttpStatusCode } from '@shared/core-utils/miscs/http-error-codes'
import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models' import { Account, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
import { import {
addVideoInPlaylist,
cleanupTests, cleanupTests,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
getVideosList, getVideosList,
@ -82,23 +80,17 @@ describe('Test a client controllers', function () {
// Playlist // Playlist
const playlistAttrs = { const attributes = {
displayName: playlistName, displayName: playlistName,
description: playlistDescription, description: playlistDescription,
privacy: VideoPlaylistPrivacy.PUBLIC, privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: servers[0].videoChannel.id videoChannelId: servers[0].videoChannel.id
} }
const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) playlist = await servers[0].playlistsCommand.create({ attributes })
playlist = resVideoPlaylistRequest.body.videoPlaylist
playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ] playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
await addVideoInPlaylist({ await servers[0].playlistsCommand.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: video.id } })
url: servers[0].url,
token: servers[0].accessToken,
playlistId: playlist.shortUUID,
elementAttrs: { videoId: video.id }
})
// Account // Account

View File

@ -5,10 +5,8 @@ import { ServerHookName, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/mode
import { import {
addVideoCommentReply, addVideoCommentReply,
addVideoCommentThread, addVideoCommentThread,
addVideoInPlaylist,
blockUser, blockUser,
createUser, createUser,
createVideoPlaylist,
deleteVideoComment, deleteVideoComment,
PluginsCommand, PluginsCommand,
registerUser, registerUser,
@ -180,15 +178,13 @@ describe('Test plugin action hooks', function () {
before(async function () { before(async function () {
{ {
const res = await createVideoPlaylist({ const { id } = await servers[0].playlistsCommand.create({
url: servers[0].url, attributes: {
token: servers[0].accessToken,
playlistAttrs: {
displayName: 'My playlist', displayName: 'My playlist',
privacy: VideoPlaylistPrivacy.PRIVATE privacy: VideoPlaylistPrivacy.PRIVATE
} }
}) })
playlistId = res.body.videoPlaylist.id playlistId = id
} }
{ {
@ -198,12 +194,7 @@ describe('Test plugin action hooks', function () {
}) })
it('Should run action:api.video-playlist-element.created', async function () { it('Should run action:api.video-playlist-element.created', async function () {
await addVideoInPlaylist({ await servers[0].playlistsCommand.addElement({ playlistId, attributes: { videoId } })
url: servers[0].url,
token: servers[0].accessToken,
playlistId,
elementAttrs: { videoId }
})
await checkHook('action:api.video-playlist-element.created') await checkHook('action:api.video-playlist-element.created')
}) })

View File

@ -7,7 +7,6 @@ import {
addVideoCommentReply, addVideoCommentReply,
addVideoCommentThread, addVideoCommentThread,
cleanupTests, cleanupTests,
createVideoPlaylist,
doubleFollow, doubleFollow,
flushAndRunMultipleServers, flushAndRunMultipleServers,
getAccountVideos, getAccountVideos,
@ -15,7 +14,6 @@ import {
getVideo, getVideo,
getVideoChannelVideos, getVideoChannelVideos,
getVideoCommentThreads, getVideoCommentThreads,
getVideoPlaylist,
getVideosList, getVideosList,
getVideosListPagination, getVideosListPagination,
getVideoThreadComments, getVideoThreadComments,
@ -443,11 +441,11 @@ describe('Test plugin filter hooks', function () {
} }
{ {
const playlistAttrs = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC } const attributes = { displayName: name, videoChannelId: servers[0].videoChannel.id, privacy: VideoPlaylistPrivacy.PUBLIC }
const res = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs }) const { id } = await servers[0].playlistsCommand.create({ attributes })
const resPlaylist = await getVideoPlaylist(servers[0].url, res.body.videoPlaylist.id) const playlist = await servers[0].playlistsCommand.get({ playlistId: id })
embedPlaylists.push(resPlaylist.body) embedPlaylists.push(playlist)
} }
} }
}) })

View File

@ -5,13 +5,11 @@ import * as chai from 'chai'
import { HttpStatusCode } from '@shared/core-utils' import { HttpStatusCode } from '@shared/core-utils'
import { import {
cleanupTests, cleanupTests,
createVideoPlaylist,
flushAndRunServer, flushAndRunServer,
getVideo, getVideo,
getVideoCategories, getVideoCategories,
getVideoLanguages, getVideoLanguages,
getVideoLicences, getVideoLicences,
getVideoPlaylistPrivacies,
getVideoPrivacies, getVideoPrivacies,
PluginsCommand, PluginsCommand,
ServerInfo, ServerInfo,
@ -79,8 +77,7 @@ describe('Test plugin altering video constants', function () {
}) })
it('Should have updated playlist privacies', async function () { it('Should have updated playlist privacies', async function () {
const res = await getVideoPlaylistPrivacies(server.url) const playlistPrivacies = await server.playlistsCommand.getPrivacies()
const playlistPrivacies = res.body
expect(playlistPrivacies[1]).to.exist expect(playlistPrivacies[1]).to.exist
expect(playlistPrivacies[2]).to.exist expect(playlistPrivacies[2]).to.exist
@ -93,13 +90,8 @@ describe('Test plugin altering video constants', function () {
}) })
it('Should not be able to create a video with this privacy', async function () { it('Should not be able to create a video with this privacy', async function () {
const attrs = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE } const attributes = { displayName: 'video playlist', privacy: VideoPlaylistPrivacy.PRIVATE }
await createVideoPlaylist({ await server.playlistsCommand.create({ attributes, expectedStatus: HttpStatusCode.BAD_REQUEST_400 })
url: server.url,
token: server.accessToken,
playlistAttrs: attrs,
expectedStatus: HttpStatusCode.BAD_REQUEST_400
})
}) })
it('Should be able to upload a video with these values', async function () { it('Should be able to upload a video with these values', async function () {
@ -162,8 +154,7 @@ describe('Test plugin altering video constants', function () {
} }
{ {
const res = await getVideoPlaylistPrivacies(server.url) const playlistPrivacies = await server.playlistsCommand.getPrivacies()
const playlistPrivacies = res.body
expect(playlistPrivacies[1]).to.exist expect(playlistPrivacies[1]).to.exist
expect(playlistPrivacies[2]).to.exist expect(playlistPrivacies[2]).to.exist

View File

@ -18,7 +18,7 @@ import { makeGetRequest } from '../requests/requests'
import { SearchCommand } from '../search' import { SearchCommand } from '../search'
import { SocketIOCommand } from '../socket' import { SocketIOCommand } from '../socket'
import { AccountsCommand, BlocklistCommand, SubscriptionsCommand } from '../users' import { AccountsCommand, BlocklistCommand, SubscriptionsCommand } from '../users'
import { BlacklistCommand, CaptionsCommand, ChangeOwnershipCommand, LiveCommand, ServicesCommand } from '../videos' import { BlacklistCommand, CaptionsCommand, ChangeOwnershipCommand, LiveCommand, PlaylistsCommand, ServicesCommand } from '../videos'
import { ConfigCommand } from './config-command' import { ConfigCommand } from './config-command'
import { ContactFormCommand } from './contact-form-command' import { ContactFormCommand } from './contact-form-command'
import { DebugCommand } from './debug-command' import { DebugCommand } from './debug-command'
@ -105,6 +105,7 @@ interface ServerInfo {
blacklistCommand?: BlacklistCommand blacklistCommand?: BlacklistCommand
captionsCommand?: CaptionsCommand captionsCommand?: CaptionsCommand
changeOwnershipCommand?: ChangeOwnershipCommand changeOwnershipCommand?: ChangeOwnershipCommand
playlistsCommand?: PlaylistsCommand
} }
function parallelTests () { function parallelTests () {
@ -335,6 +336,7 @@ async function runServer (server: ServerInfo, configOverrideArg?: any, args = []
server.blacklistCommand = new BlacklistCommand(server) server.blacklistCommand = new BlacklistCommand(server)
server.captionsCommand = new CaptionsCommand(server) server.captionsCommand = new CaptionsCommand(server)
server.changeOwnershipCommand = new ChangeOwnershipCommand(server) server.changeOwnershipCommand = new ChangeOwnershipCommand(server)
server.playlistsCommand = new PlaylistsCommand(server)
res(server) res(server)
}) })

View File

@ -4,11 +4,12 @@ export * from './captions-command'
export * from './change-ownership-command' export * from './change-ownership-command'
export * from './live-command' export * from './live-command'
export * from './live' export * from './live'
export * from './playlists-command'
export * from './playlists'
export * from './services-command' export * from './services-command'
export * from './video-channels' export * from './video-channels'
export * from './video-comments' export * from './video-comments'
export * from './video-history' export * from './video-history'
export * from './video-imports' export * from './video-imports'
export * from './video-playlists'
export * from './video-streaming-playlists' export * from './video-streaming-playlists'
export * from './videos' export * from './videos'

View File

@ -0,0 +1,280 @@
import { omit, pick } from 'lodash'
import {
BooleanBothQuery,
ResultList,
VideoExistInPlaylist,
VideoPlaylist,
VideoPlaylistCreateResult,
VideoPlaylistElement,
VideoPlaylistElementCreateResult,
VideoPlaylistReorder
} from '@shared/models'
import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
import { unwrapBody } from '../requests'
import { AbstractCommand, OverrideCommandOptions } from '../shared'
import { videoUUIDToId } from './videos'
export class PlaylistsCommand extends AbstractCommand {
list (options: OverrideCommandOptions & {
start?: number
count?: number
sort?: string
}) {
const path = '/api/v1/video-playlists'
const query = pick(options, [ 'start', 'count', 'sort' ])
return this.getRequestBody<ResultList<VideoPlaylist>>({
...options,
path,
query,
implicitToken: false,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
listByChannel (options: OverrideCommandOptions & {
handle: string
start?: number
count?: number
sort?: string
}) {
const path = '/api/v1/video-channels/' + options.handle + '/video-playlists'
const query = pick(options, [ 'start', 'count', 'sort' ])
return this.getRequestBody<ResultList<VideoPlaylist>>({
...options,
path,
query,
implicitToken: false,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
listByAccount (options: OverrideCommandOptions & {
handle: string
start?: number
count?: number
sort?: string
search?: string
playlistType?: VideoPlaylistType
}) {
const path = '/api/v1/accounts/' + options.handle + '/video-playlists'
const query = pick(options, [ 'start', 'count', 'sort', 'search', 'playlistType' ])
return this.getRequestBody<ResultList<VideoPlaylist>>({
...options,
path,
query,
implicitToken: false,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
get (options: OverrideCommandOptions & {
playlistId: number | string
}) {
const { playlistId } = options
const path = '/api/v1/video-playlists/' + playlistId
return this.getRequestBody<VideoPlaylist>({
...options,
path,
implicitToken: false,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
listVideos (options: OverrideCommandOptions & {
playlistId: number | string
start?: number
count?: number
query?: { nsfw?: BooleanBothQuery }
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
const query = options.query ?? {}
return this.getRequestBody<ResultList<VideoPlaylistElement>>({
...options,
path,
query: {
...query,
start: options.start,
count: options.count
},
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
delete (options: OverrideCommandOptions & {
playlistId: number | string
}) {
const path = '/api/v1/video-playlists/' + options.playlistId
return this.deleteRequest({
...options,
path,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
})
}
async create (options: OverrideCommandOptions & {
attributes: VideoPlaylistCreate
}) {
const path = '/api/v1/video-playlists'
const fields = omit(options.attributes, 'thumbnailfile')
const attaches = options.attributes.thumbnailfile
? { thumbnailfile: options.attributes.thumbnailfile }
: {}
const body = await unwrapBody<{ videoPlaylist: VideoPlaylistCreateResult }>(this.postUploadRequest({
...options,
path,
fields,
attaches,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.OK_200
}))
return body.videoPlaylist
}
update (options: OverrideCommandOptions & {
attributes: VideoPlaylistUpdate
playlistId: number | string
}) {
const path = '/api/v1/video-playlists/' + options.playlistId
const fields = omit(options.attributes, 'thumbnailfile')
const attaches = options.attributes.thumbnailfile
? { thumbnailfile: options.attributes.thumbnailfile }
: {}
return this.putUploadRequest({
...options,
path,
fields,
attaches,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
})
}
async addElement (options: OverrideCommandOptions & {
playlistId: number | string
attributes: VideoPlaylistElementCreate | { videoId: string }
}) {
const attributes = {
...options.attributes,
videoId: await videoUUIDToId(this.server.url, options.attributes.videoId)
}
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
const body = await unwrapBody<{ videoPlaylistElement: VideoPlaylistElementCreateResult }>(this.postBodyRequest({
...options,
path,
fields: attributes,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.OK_200
}))
return body.videoPlaylistElement
}
updateElement (options: OverrideCommandOptions & {
playlistId: number | string
elementId: number | string
attributes: VideoPlaylistElementUpdate
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId
return this.putBodyRequest({
...options,
path,
fields: options.attributes,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
})
}
removeElement (options: OverrideCommandOptions & {
playlistId: number | string
elementId: number
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.elementId
return this.deleteRequest({
...options,
path,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
})
}
reorderElements (options: OverrideCommandOptions & {
playlistId: number | string
attributes: VideoPlaylistReorder
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
return this.postBodyRequest({
...options,
path,
fields: options.attributes,
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
})
}
getPrivacies (options: OverrideCommandOptions = {}) {
const path = '/api/v1/video-playlists/privacies'
return this.getRequestBody<{ [ id: number ]: string }>({
...options,
path,
implicitToken: false,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
videosExist (options: OverrideCommandOptions & {
videoIds: number[]
}) {
const { videoIds } = options
const path = '/api/v1/users/me/video-playlists/videos-exist'
return this.getRequestBody<VideoExistInPlaylist>({
...options,
path,
query: { videoIds },
implicitToken: true,
defaultExpectedStatus: HttpStatusCode.OK_200
})
}
}

View File

@ -0,0 +1,25 @@
import { expect } from 'chai'
import { readdir } from 'fs-extra'
import { join } from 'path'
import { root } from '../'
async function checkPlaylistFilesWereRemoved (
playlistUUID: string,
internalServerNumber: number,
directories = [ 'thumbnails' ]
) {
const testDirectory = 'test' + internalServerNumber
for (const directory of directories) {
const directoryPath = join(root(), testDirectory, directory)
const files = await readdir(directoryPath)
for (const file of files) {
expect(file).to.not.contain(playlistUUID)
}
}
}
export {
checkPlaylistFilesWereRemoved
}

View File

@ -1,320 +0,0 @@
import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
import { omit } from 'lodash'
import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
import { videoUUIDToId } from './videos'
import { join } from 'path'
import { root } from '..'
import { readdir } from 'fs-extra'
import { expect } from 'chai'
import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
function getVideoPlaylistsList (url: string, start: number, count: number, sort?: string) {
const path = '/api/v1/video-playlists'
const query = {
start,
count,
sort
}
return makeGetRequest({
url,
path,
query,
statusCodeExpected: HttpStatusCode.OK_200
})
}
function getVideoChannelPlaylistsList (url: string, videoChannelName: string, start: number, count: number, sort?: string) {
const path = '/api/v1/video-channels/' + videoChannelName + '/video-playlists'
const query = {
start,
count,
sort
}
return makeGetRequest({
url,
path,
query,
statusCodeExpected: HttpStatusCode.OK_200
})
}
function getAccountPlaylistsList (url: string, accountName: string, start: number, count: number, sort?: string, search?: string) {
const path = '/api/v1/accounts/' + accountName + '/video-playlists'
const query = {
start,
count,
sort,
search
}
return makeGetRequest({
url,
path,
query,
statusCodeExpected: HttpStatusCode.OK_200
})
}
function getAccountPlaylistsListWithToken (
url: string,
token: string,
accountName: string,
start: number,
count: number,
playlistType?: VideoPlaylistType,
sort?: string
) {
const path = '/api/v1/accounts/' + accountName + '/video-playlists'
const query = {
start,
count,
playlistType,
sort
}
return makeGetRequest({
url,
token,
path,
query,
statusCodeExpected: HttpStatusCode.OK_200
})
}
function getVideoPlaylist (url: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
const path = '/api/v1/video-playlists/' + playlistId
return makeGetRequest({
url,
path,
statusCodeExpected
})
}
function getVideoPlaylistWithToken (url: string, token: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
const path = '/api/v1/video-playlists/' + playlistId
return makeGetRequest({
url,
token,
path,
statusCodeExpected
})
}
function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = HttpStatusCode.NO_CONTENT_204) {
const path = '/api/v1/video-playlists/' + playlistId
return makeDeleteRequest({
url,
path,
token,
statusCodeExpected
})
}
function createVideoPlaylist (options: {
url: string
token: string
playlistAttrs: VideoPlaylistCreate
expectedStatus?: number
}) {
const path = '/api/v1/video-playlists'
const fields = omit(options.playlistAttrs, 'thumbnailfile')
const attaches = options.playlistAttrs.thumbnailfile
? { thumbnailfile: options.playlistAttrs.thumbnailfile }
: {}
return makeUploadRequest({
method: 'POST',
url: options.url,
path,
token: options.token,
fields,
attaches,
statusCodeExpected: options.expectedStatus || HttpStatusCode.OK_200
})
}
function updateVideoPlaylist (options: {
url: string
token: string
playlistAttrs: VideoPlaylistUpdate
playlistId: number | string
expectedStatus?: number
}) {
const path = '/api/v1/video-playlists/' + options.playlistId
const fields = omit(options.playlistAttrs, 'thumbnailfile')
const attaches = options.playlistAttrs.thumbnailfile
? { thumbnailfile: options.playlistAttrs.thumbnailfile }
: {}
return makeUploadRequest({
method: 'PUT',
url: options.url,
path,
token: options.token,
fields,
attaches,
statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
})
}
async function addVideoInPlaylist (options: {
url: string
token: string
playlistId: number | string
elementAttrs: VideoPlaylistElementCreate | { videoId: string }
expectedStatus?: number
}) {
options.elementAttrs.videoId = await videoUUIDToId(options.url, options.elementAttrs.videoId)
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
return makePostBodyRequest({
url: options.url,
path,
token: options.token,
fields: options.elementAttrs,
statusCodeExpected: options.expectedStatus || HttpStatusCode.OK_200
})
}
function updateVideoPlaylistElement (options: {
url: string
token: string
playlistId: number | string
playlistElementId: number | string
elementAttrs: VideoPlaylistElementUpdate
expectedStatus?: number
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.playlistElementId
return makePutBodyRequest({
url: options.url,
path,
token: options.token,
fields: options.elementAttrs,
statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
})
}
function removeVideoFromPlaylist (options: {
url: string
token: string
playlistId: number | string
playlistElementId: number
expectedStatus?: number
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.playlistElementId
return makeDeleteRequest({
url: options.url,
path,
token: options.token,
statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
})
}
function reorderVideosPlaylist (options: {
url: string
token: string
playlistId: number | string
elementAttrs: {
startPosition: number
insertAfterPosition: number
reorderLength?: number
}
expectedStatus?: number
}) {
const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
return makePostBodyRequest({
url: options.url,
path,
token: options.token,
fields: options.elementAttrs,
statusCodeExpected: options.expectedStatus || HttpStatusCode.NO_CONTENT_204
})
}
async function checkPlaylistFilesWereRemoved (
playlistUUID: string,
internalServerNumber: number,
directories = [ 'thumbnails' ]
) {
const testDirectory = 'test' + internalServerNumber
for (const directory of directories) {
const directoryPath = join(root(), testDirectory, directory)
const files = await readdir(directoryPath)
for (const file of files) {
expect(file).to.not.contain(playlistUUID)
}
}
}
function getVideoPlaylistPrivacies (url: string) {
const path = '/api/v1/video-playlists/privacies'
return makeGetRequest({
url,
path,
statusCodeExpected: HttpStatusCode.OK_200
})
}
function doVideosExistInMyPlaylist (url: string, token: string, videoIds: number[]) {
const path = '/api/v1/users/me/video-playlists/videos-exist'
return makeGetRequest({
url,
token,
path,
query: { videoIds },
statusCodeExpected: HttpStatusCode.OK_200
})
}
// ---------------------------------------------------------------------------
export {
getVideoPlaylistPrivacies,
getVideoPlaylistsList,
getVideoChannelPlaylistsList,
getAccountPlaylistsList,
getAccountPlaylistsListWithToken,
getVideoPlaylist,
getVideoPlaylistWithToken,
createVideoPlaylist,
updateVideoPlaylist,
deleteVideoPlaylist,
addVideoInPlaylist,
updateVideoPlaylistElement,
removeVideoFromPlaylist,
reorderVideosPlaylist,
checkPlaylistFilesWereRemoved,
doVideosExistInMyPlaylist
}

View File

@ -262,28 +262,6 @@ function getVideoChannelVideos (
}) })
} }
function getPlaylistVideos (
url: string,
accessToken: string,
playlistId: number | string,
start: number,
count: number,
query: { nsfw?: boolean } = {}
) {
const path = '/api/v1/video-playlists/' + playlistId + '/videos'
return makeGetRequest({
url,
path,
query: immutableAssign(query, {
start,
count
}),
token: accessToken,
statusCodeExpected: HttpStatusCode.OK_200
})
}
function getVideosListPagination (url: string, start: number, count: number, sort?: string, skipCount?: boolean) { function getVideosListPagination (url: string, start: number, count: number, sort?: string, skipCount?: boolean) {
const path = '/api/v1/videos' const path = '/api/v1/videos'
@ -871,7 +849,6 @@ export {
getLocalVideos, getLocalVideos,
completeVideoCheck, completeVideoCheck,
checkVideoFilesWereRemoved, checkVideoFilesWereRemoved,
getPlaylistVideos,
getMyVideosWithFilter, getMyVideosWithFilter,
uploadVideoAndGetId, uploadVideoAndGetId,
getLocalIdByUUID, getLocalIdByUUID,

View File

@ -1,6 +1,7 @@
export * from './video-exist-in-playlist.model' export * from './video-exist-in-playlist.model'
export * from './video-playlist-create-result.model' export * from './video-playlist-create-result.model'
export * from './video-playlist-create.model' export * from './video-playlist-create.model'
export * from './video-playlist-element-create-result.model'
export * from './video-playlist-element-create.model' export * from './video-playlist-element-create.model'
export * from './video-playlist-element-update.model' export * from './video-playlist-element-update.model'
export * from './video-playlist-element.model' export * from './video-playlist-element.model'

View File

@ -0,0 +1,3 @@
export interface VideoPlaylistElementCreateResult {
id: number
}