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 { getServerActor } from '@server/models/application/application'
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 { VideoPlaylistCreate } from '../../../shared/models/videos/playlist/video-playlist-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,
shortUUID: uuidToShort(videoPlaylistCreated.uuid),
uuid: videoPlaylistCreated.uuid
}
} as VideoPlaylistCreateResult
})
}
@ -338,8 +339,8 @@ async function addVideoInPlaylist (req: express.Request, res: express.Response)
return res.json({
videoPlaylistElement: {
id: playlistElement.id
}
}).end()
} as VideoPlaylistElementCreateResult
})
}
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 {
cleanupTests,
createVideoPlaylist,
doubleFollow,
flushAndRunMultipleServers,
makeActivityPubGetRequest,
@ -74,9 +73,8 @@ describe('Test activitypub', function () {
}
{
const playlistAttrs = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id }
const resCreate = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
playlist = resCreate.body.videoPlaylist
const attributes = { displayName: 'playlist', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[0].videoChannel.id }
playlist = await servers[0].playlistsCommand.create({ attributes })
}
await doubleFollow(servers[0], servers[1])

View File

@ -5,12 +5,10 @@ import { HttpStatusCode } from '@shared/core-utils'
import {
cleanupTests,
closeAllSequelize,
createVideoPlaylist,
doubleFollow,
flushAndRunMultipleServers,
generateUserAccessToken,
getVideo,
getVideoPlaylist,
killallServers,
reRunServer,
ServerInfo,
@ -58,15 +56,15 @@ describe('Test AP refresher', function () {
}
{
const playlistAttrs = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id }
const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs })
playlistUUID1 = res.body.videoPlaylist.uuid
const attributes = { displayName: 'playlist1', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id }
const created = await servers[1].playlistsCommand.create({ attributes })
playlistUUID1 = created.uuid
}
{
const playlistAttrs = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id }
const res = await createVideoPlaylist({ url: servers[1].url, token: servers[1].accessToken, playlistAttrs })
playlistUUID2 = res.body.videoPlaylist.uuid
const attributes = { displayName: 'playlist2', privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: servers[1].videoChannel.id }
const created = await servers[1].playlistsCommand.create({ attributes })
playlistUUID2 = created.uuid
}
await doubleFollow(servers[0], servers[1])
@ -144,13 +142,13 @@ describe('Test AP refresher', function () {
// Change UUID so the remote server returns a 404
await setPlaylistField(servers[1].internalServerNumber, playlistUUID2, 'uuid', '304afe4f-39f9-4d49-8ed7-ac57b86b178e')
await getVideoPlaylist(servers[0].url, playlistUUID1)
await getVideoPlaylist(servers[0].url, playlistUUID2)
await servers[0].playlistsCommand.get({ playlistId: playlistUUID1 })
await servers[0].playlistsCommand.get({ playlistId: playlistUUID2 })
await waitJobs(servers)
await getVideoPlaylist(servers[0].url, playlistUUID1, HttpStatusCode.OK_200)
await getVideoPlaylist(servers[0].url, playlistUUID2, HttpStatusCode.NOT_FOUND_404)
await servers[0].playlistsCommand.get({ playlistId: playlistUUID1, expectedStatus: HttpStatusCode.OK_200 })
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 */
import 'mocha'
import { VideoPlaylistPrivacy } from '@shared/models'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import {
cleanupTests,
flushAndRunServer,
makeGetRequest,
ServerInfo,
setAccessTokensToServers,
uploadVideo,
createVideoPlaylist,
setDefaultVideoChannel
setDefaultVideoChannel,
uploadVideo
} 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 () {
let server: ServerInfo
@ -34,17 +32,15 @@ describe('Test services API validators', function () {
}
{
const res = await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
const created = await server.playlistsCommand.create({
attributes: {
displayName: 'super playlist',
privacy: VideoPlaylistPrivacy.PUBLIC,
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 */
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 {
addVideoInPlaylist,
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination,
cleanupTests,
createVideoPlaylist,
deleteVideoPlaylist,
flushAndRunServer,
generateUserAccessToken,
getAccountPlaylistsListWithToken,
getVideoPlaylist,
immutableAssign,
makeGetRequest,
removeVideoFromPlaylist,
reorderVideosPlaylist,
PlaylistsCommand,
ServerInfo,
setAccessTokensToServers,
setDefaultVideoChannel,
updateVideoPlaylist,
updateVideoPlaylistElement,
uploadVideoAndGetId
} from '../../../../shared/extra-utils'
@ -36,7 +36,9 @@ describe('Test video playlists API validator', function () {
let watchLaterPlaylistId: 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')
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)
watchLaterPlaylistId = res.body.data[0].id
const { data } = await command.listByAccount({
token: server.accessToken,
handle: 'root',
start: 0,
count: 5,
playlistType: VideoPlaylistType.WATCH_LATER
})
watchLaterPlaylistId = data[0].id
}
{
const res = await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
playlist = await command.create({
attributes: {
displayName: 'super playlist',
privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: server.videoChannel.id
}
})
playlist = res.body.videoPlaylist
}
{
const res = await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
const created = await command.create({
attributes: {
displayName: '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 () {
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 () {
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 () {
const res = await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
const playlist = await command.create({
attributes: {
displayName: 'super playlist',
videoChannelId: server.videoChannel.id,
privacy: VideoPlaylistPrivacy.UNLISTED
}
})
const playlist = res.body.videoPlaylist
await getVideoPlaylist(server.url, playlist.id, HttpStatusCode.NOT_FOUND_404)
await getVideoPlaylist(server.url, playlist.uuid, HttpStatusCode.OK_200)
await command.get({ playlistId: playlist.id, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await command.get({ playlistId: playlist.uuid, expectedStatus: HttpStatusCode.OK_200 })
})
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 () {
const getBase = (playlistAttrs: any = {}, wrapper: any = {}) => {
return Object.assign({
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
url: server.url,
token: server.accessToken,
playlistAttrs: Object.assign({
const getBase = (
attributes?: Partial<VideoPlaylistCreate>,
wrapper?: Partial<Parameters<PlaylistsCommand['create']>[0]>
) => {
return {
attributes: {
displayName: 'display name',
privacy: VideoPlaylistPrivacy.UNLISTED,
thumbnailfile: 'thumbnail.jpg',
videoChannelId: server.videoChannel.id
}, playlistAttrs)
}, wrapper)
videoChannelId: server.videoChannel.id,
...attributes
},
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
...wrapper
}
}
const getUpdate = (params: any, playlistId: number | string) => {
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 () {
const params = getBase({}, { token: null, expectedStatus: HttpStatusCode.UNAUTHORIZED_401 })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail without displayName', async function () {
const params = getBase({ displayName: undefined })
await createVideoPlaylist(params)
await command.create(params)
})
it('Should fail with an incorrect display name', async function () {
const params = getBase({ displayName: 's'.repeat(300) })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail with an incorrect description', async function () {
const params = getBase({ description: 't' })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail with an incorrect privacy', async function () {
const params = getBase({ privacy: 45 })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail with an unknown video channel id', async function () {
const params = getBase({ videoChannelId: 42 }, { expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail with an incorrect thumbnail file', async function () {
const params = getBase({ thumbnailfile: 'video_short.mp4' })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail with a thumbnail file too big', async function () {
const params = getBase({ thumbnailfile: 'preview-big.png' })
await createVideoPlaylist(params)
await updateVideoPlaylist(getUpdate(params, playlist.shortUUID))
await command.create(params)
await command.update(getUpdate(params, playlist.shortUUID))
})
it('Should fail to set "public" a playlist not assigned to a channel', async function () {
const params = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: undefined })
const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' })
const params3 = getBase({ privacy: undefined, videoChannelId: 'null' })
const params2 = getBase({ privacy: VideoPlaylistPrivacy.PUBLIC, videoChannelId: 'null' as any })
const params3 = getBase({ privacy: undefined, videoChannelId: 'null' as any })
await createVideoPlaylist(params)
await createVideoPlaylist(params2)
await updateVideoPlaylist(getUpdate(params, privatePlaylistUUID))
await updateVideoPlaylist(getUpdate(params2, playlist.shortUUID))
await updateVideoPlaylist(getUpdate(params3, playlist.shortUUID))
await command.create(params)
await command.create(params2)
await command.update(getUpdate(params, privatePlaylistUUID))
await command.update(getUpdate(params2, playlist.shortUUID))
await command.update(getUpdate(params3, playlist.shortUUID))
})
it('Should fail with an unknown playlist to update', async function () {
await updateVideoPlaylist(getUpdate(
await command.update(getUpdate(
getBase({}, { expectedStatus: HttpStatusCode.NOT_FOUND_404 }),
42
))
})
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 }),
playlist.shortUUID
))
})
it('Should fail to update the watch later playlist', async function () {
await updateVideoPlaylist(getUpdate(
await command.update(getUpdate(
getBase({}, { expectedStatus: HttpStatusCode.BAD_REQUEST_400 }),
watchLaterPlaylistId
))
@ -300,146 +308,158 @@ describe('Test video playlists API validator', function () {
it('Should succeed with the correct params', async function () {
{
const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
await createVideoPlaylist(params)
await command.create(params)
}
{
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 () {
const getBase = (elementAttrs: any = {}, wrapper: any = {}) => {
return Object.assign({
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
url: server.url,
token: server.accessToken,
playlistId: playlist.id,
elementAttrs: Object.assign({
const getBase = (
attributes?: Partial<VideoPlaylistElementCreate>,
wrapper?: Partial<Parameters<PlaylistsCommand['addElement']>[0]>
) => {
return {
attributes: {
videoId,
startTimestamp: 2,
stopTimestamp: 3
}, elementAttrs)
}, wrapper)
stopTimestamp: 3,
...attributes
},
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
playlistId: playlist.id,
...wrapper
}
}
it('Should fail with an unauthenticated user', async function () {
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 () {
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 () {
{
const params = getBase({}, { playlistId: 'toto' })
await addVideoInPlaylist(params)
await command.addElement(params)
}
{
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 () {
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 () {
{
const params = getBase({ startTimestamp: -42 })
await addVideoInPlaylist(params)
await command.addElement(params)
}
{
const params = getBase({ stopTimestamp: 'toto' as any })
await addVideoInPlaylist(params)
await command.addElement(params)
}
})
it('Succeed with the correct params', async function () {
const params = getBase({}, { expectedStatus: HttpStatusCode.OK_200 })
const res = await addVideoInPlaylist(params)
playlistElementId = res.body.videoPlaylistElement.id
const created = await command.addElement(params)
elementId = created.id
})
})
describe('When updating an element in a playlist', function () {
const getBase = (elementAttrs: any = {}, wrapper: any = {}) => {
return Object.assign({
url: server.url,
token: server.accessToken,
elementAttrs: Object.assign({
const getBase = (
attributes?: Partial<VideoPlaylistElementUpdate>,
wrapper?: Partial<Parameters<PlaylistsCommand['updateElement']>[0]>
) => {
return {
attributes: {
startTimestamp: 1,
stopTimestamp: 2
}, elementAttrs),
playlistElementId,
stopTimestamp: 2,
...attributes
},
elementId,
playlistId: playlist.id,
expectedStatus: HttpStatusCode.BAD_REQUEST_400
}, wrapper)
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
...wrapper
}
}
it('Should fail with an unauthenticated user', async function () {
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 () {
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 () {
{
const params = getBase({}, { playlistId: 'toto' })
await updateVideoPlaylistElement(params)
await command.updateElement(params)
}
{
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 () {
{
const params = getBase({}, { playlistElementId: 'toto' })
await updateVideoPlaylistElement(params)
const params = getBase({}, { elementId: 'toto' })
await command.updateElement(params)
}
{
const params = getBase({}, { playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await updateVideoPlaylistElement(params)
const params = getBase({}, { elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await command.updateElement(params)
}
})
it('Should fail with a bad start/stop timestamp', async function () {
{
const params = getBase({ startTimestamp: 'toto' as any })
await updateVideoPlaylistElement(params)
await command.updateElement(params)
}
{
const params = getBase({ stopTimestamp: -42 })
await updateVideoPlaylistElement(params)
await command.updateElement(params)
}
})
it('Should fail with an unknown element', async function () {
const params = getBase({}, { playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await updateVideoPlaylistElement(params)
const params = getBase({}, { elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await command.updateElement(params)
})
it('Succeed with the correct params', async function () {
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 videoId4: number
const getBase = (elementAttrs: any = {}, wrapper: any = {}) => {
return Object.assign({
url: server.url,
token: server.accessToken,
playlistId: playlist.shortUUID,
elementAttrs: Object.assign({
const getBase = (
attributes?: Partial<VideoPlaylistReorder>,
wrapper?: Partial<Parameters<PlaylistsCommand['reorderElements']>[0]>
) => {
return {
attributes: {
startPosition: 1,
insertAfterPosition: 2,
reorderLength: 3
}, elementAttrs),
expectedStatus: HttpStatusCode.BAD_REQUEST_400
}, wrapper)
reorderLength: 3,
...attributes
},
playlistId: playlist.shortUUID,
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
...wrapper
}
}
before(async function () {
@ -466,91 +492,86 @@ describe('Test video playlists API validator', function () {
videoId4 = (await uploadVideoAndGetId({ server, videoName: 'video 4' })).id
for (const id of [ videoId3, videoId4 ]) {
await addVideoInPlaylist({
url: server.url,
token: server.accessToken,
playlistId: playlist.shortUUID,
elementAttrs: { videoId: id }
})
await command.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: id } })
}
})
it('Should fail with an unauthenticated user', async function () {
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 () {
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 () {
{
const params = getBase({}, { playlistId: 'toto' })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
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 () {
{
const params = getBase({ startPosition: -1 })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
const params = getBase({ startPosition: 'toto' as any })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
const params = getBase({ startPosition: 42 })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
})
it('Should fail with an invalid insert after position', async function () {
{
const params = getBase({ insertAfterPosition: 'toto' as any })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
const params = getBase({ insertAfterPosition: -2 })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
const params = getBase({ insertAfterPosition: 42 })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
})
it('Should fail with an invalid reorder length', async function () {
{
const params = getBase({ reorderLength: 'toto' as any })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
const params = getBase({ reorderLength: -2 })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
{
const params = getBase({ reorderLength: 42 })
await reorderVideosPlaylist(params)
await command.reorderElements(params)
}
})
it('Succeed with the correct params', async function () {
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 () {
const getBase = (wrapper: any = {}) => {
return Object.assign({
url: server.url,
token: server.accessToken,
playlistElementId,
const getBase = (wrapper: Partial<Parameters<PlaylistsCommand['removeElement']>[0]>) => {
return {
elementId,
playlistId: playlist.uuid,
expectedStatus: HttpStatusCode.BAD_REQUEST_400
}, wrapper)
expectedStatus: HttpStatusCode.BAD_REQUEST_400,
...wrapper
}
}
it('Should fail with an unauthenticated user', async function () {
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 () {
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 () {
{
const params = getBase({ playlistId: 'toto' })
await removeVideoFromPlaylist(params)
await command.removeElement(params)
}
{
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 () {
{
const params = getBase({ playlistElementId: 'toto' })
await removeVideoFromPlaylist(params)
const params = getBase({ elementId: 'toto' as any })
await command.removeElement(params)
}
{
const params = getBase({ playlistElementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await removeVideoFromPlaylist(params)
const params = getBase({ elementId: 42, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await command.removeElement(params)
}
})
it('Should fail with an unknown element', async function () {
const params = getBase({ playlistElementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await removeVideoFromPlaylist(params)
const params = getBase({ elementId: 888, expectedStatus: HttpStatusCode.NOT_FOUND_404 })
await command.removeElement(params)
})
it('Succeed with the correct params', async function () {
const params = getBase({ expectedStatus: HttpStatusCode.NO_CONTENT_204 })
await removeVideoFromPlaylist(params)
await command.removeElement(params)
})
})
describe('When deleting a playlist', 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 () {
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 () {
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 () {
await deleteVideoPlaylist(server.url, server.accessToken, playlist.uuid)
await command.delete({ playlistId: playlist.uuid })
})
})

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,6 @@ import {
buildServerDirectory,
cleanupTests,
CLICommand,
createVideoPlaylist,
doubleFollow,
flushAndRunMultipleServers,
killallServers,
@ -77,10 +76,8 @@ describe('Test prune storage scripts', function () {
await updateMyAvatar({ url: server.url, accessToken: server.accessToken, fixture: 'avatar.png' })
await createVideoPlaylist({
url: server.url,
token: server.accessToken,
playlistAttrs: {
await server.playlistsCommand.create({
attributes: {
displayName: 'playlist',
privacy: VideoPlaylistPrivacy.PUBLIC,
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 { Account, HTMLServerConfig, ServerConfig, VideoPlaylistCreateResult, VideoPlaylistPrivacy } from '@shared/models'
import {
addVideoInPlaylist,
cleanupTests,
createVideoPlaylist,
doubleFollow,
flushAndRunMultipleServers,
getVideosList,
@ -82,23 +80,17 @@ describe('Test a client controllers', function () {
// Playlist
const playlistAttrs = {
const attributes = {
displayName: playlistName,
description: playlistDescription,
privacy: VideoPlaylistPrivacy.PUBLIC,
videoChannelId: servers[0].videoChannel.id
}
const resVideoPlaylistRequest = await createVideoPlaylist({ url: servers[0].url, token: servers[0].accessToken, playlistAttrs })
playlist = resVideoPlaylistRequest.body.videoPlaylist
playlist = await servers[0].playlistsCommand.create({ attributes })
playlistIds = [ playlist.id, playlist.shortUUID, playlist.uuid ]
await addVideoInPlaylist({
url: servers[0].url,
token: servers[0].accessToken,
playlistId: playlist.shortUUID,
elementAttrs: { videoId: video.id }
})
await servers[0].playlistsCommand.addElement({ playlistId: playlist.shortUUID, attributes: { videoId: video.id } })
// Account

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ import { makeGetRequest } from '../requests/requests'
import { SearchCommand } from '../search'
import { SocketIOCommand } from '../socket'
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 { ContactFormCommand } from './contact-form-command'
import { DebugCommand } from './debug-command'
@ -105,6 +105,7 @@ interface ServerInfo {
blacklistCommand?: BlacklistCommand
captionsCommand?: CaptionsCommand
changeOwnershipCommand?: ChangeOwnershipCommand
playlistsCommand?: PlaylistsCommand
}
function parallelTests () {
@ -335,6 +336,7 @@ async function runServer (server: ServerInfo, configOverrideArg?: any, args = []
server.blacklistCommand = new BlacklistCommand(server)
server.captionsCommand = new CaptionsCommand(server)
server.changeOwnershipCommand = new ChangeOwnershipCommand(server)
server.playlistsCommand = new PlaylistsCommand(server)
res(server)
})

View File

@ -4,11 +4,12 @@ export * from './captions-command'
export * from './change-ownership-command'
export * from './live-command'
export * from './live'
export * from './playlists-command'
export * from './playlists'
export * from './services-command'
export * from './video-channels'
export * from './video-comments'
export * from './video-history'
export * from './video-imports'
export * from './video-playlists'
export * from './video-streaming-playlists'
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) {
const path = '/api/v1/videos'
@ -871,7 +849,6 @@ export {
getLocalVideos,
completeVideoCheck,
checkVideoFilesWereRemoved,
getPlaylistVideos,
getMyVideosWithFilter,
uploadVideoAndGetId,
getLocalIdByUUID,

View File

@ -1,6 +1,7 @@
export * from './video-exist-in-playlist.model'
export * from './video-playlist-create-result.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-update.model'
export * from './video-playlist-element.model'

View File

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