Update video channel routes

This commit is contained in:
Chocobozzz 2018-04-24 17:05:32 +02:00
parent 82e392f8a4
commit 48dce1c90d
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
16 changed files with 1169 additions and 796 deletions

View File

@ -5,6 +5,9 @@
### BREAKING CHANGES ### BREAKING CHANGES
* Hide by default NSFW videos. Update the `instance.default_nsfw_policy` configuration to `blur` to keep the old behaviour * Hide by default NSFW videos. Update the `instance.default_nsfw_policy` configuration to `blur` to keep the old behaviour
* Move video channels routes:
* `/videos/channels` routes to `/accounts/{accountId}/video-channels`
* `/videos/accounts/{accountId}/channels` route to `/accounts/{accountId}/video-channels`
* PeerTube now listen on 127.0.0.1 by default * PeerTube now listen on 127.0.0.1 by default
* Use ISO 639 for language (*en*, *es*, *fr*...) * Use ISO 639 for language (*en*, *es*, *fr*...)
* Tools (`import-videos`...) need the language ISO639 code instead of a number * Tools (`import-videos`...) need the language ISO639 code instead of a number

View File

@ -1,11 +1,30 @@
import * as express from 'express' import * as express from 'express'
import { getFormattedObjects } from '../../helpers/utils' import { getFormattedObjects, resetSequelizeInstance } from '../../helpers/utils'
import { asyncMiddleware, optionalAuthenticate, paginationValidator, setDefaultPagination, setDefaultSort } from '../../middlewares' import {
asyncMiddleware,
authenticate,
listVideoAccountChannelsValidator,
optionalAuthenticate,
paginationValidator,
setDefaultPagination,
setDefaultSort,
videoChannelsAddValidator,
videoChannelsGetValidator,
videoChannelsRemoveValidator,
videoChannelsUpdateValidator
} from '../../middlewares'
import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators' import { accountsGetValidator, accountsSortValidator, videosSortValidator } from '../../middlewares/validators'
import { AccountModel } from '../../models/account/account' import { AccountModel } from '../../models/account/account'
import { VideoModel } from '../../models/video/video' import { VideoModel } from '../../models/video/video'
import { VideoSortField } from '../../../client/src/app/shared/video/sort-field.type'
import { isNSFWHidden } from '../../helpers/express-utils' import { isNSFWHidden } from '../../helpers/express-utils'
import { VideoChannelModel } from '../../models/video/video-channel'
import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
import { sendUpdateActor } from '../../lib/activitypub/send'
import { createVideoChannel } from '../../lib/video-channel'
import { setAsyncActorKeys } from '../../lib/activitypub'
import { sequelizeTypescript } from '../../initializers'
import { logger } from '../../helpers/logger'
import { retryTransactionWrapper } from '../../helpers/database-utils'
const accountsRouter = express.Router() const accountsRouter = express.Router()
@ -29,7 +48,45 @@ accountsRouter.get('/:id/videos',
setDefaultSort, setDefaultSort,
setDefaultPagination, setDefaultPagination,
optionalAuthenticate, optionalAuthenticate,
asyncMiddleware(getAccountVideos) asyncMiddleware(listAccountVideos)
)
accountsRouter.get('/:accountId/video-channels',
asyncMiddleware(listVideoAccountChannelsValidator),
asyncMiddleware(listVideoAccountChannels)
)
accountsRouter.post('/:accountId/video-channels',
authenticate,
videoChannelsAddValidator,
asyncMiddleware(addVideoChannelRetryWrapper)
)
accountsRouter.put('/:accountId/video-channels/:id',
authenticate,
asyncMiddleware(videoChannelsUpdateValidator),
updateVideoChannelRetryWrapper
)
accountsRouter.delete('/:accountId/video-channels/:id',
authenticate,
asyncMiddleware(videoChannelsRemoveValidator),
asyncMiddleware(removeVideoChannelRetryWrapper)
)
accountsRouter.get('/:accountId/video-channels/:id',
asyncMiddleware(videoChannelsGetValidator),
asyncMiddleware(getVideoChannel)
)
accountsRouter.get('/:accountId/video-channels/:id/videos',
asyncMiddleware(videoChannelsGetValidator),
paginationValidator,
videosSortValidator,
setDefaultSort,
setDefaultPagination,
optionalAuthenticate,
asyncMiddleware(listVideoChannelVideos)
) )
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -52,18 +109,142 @@ async function listAccounts (req: express.Request, res: express.Response, next:
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }
async function getAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) { async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const account: AccountModel = res.locals.account const resultList = await VideoChannelModel.listByAccount(res.locals.account.id)
const resultList = await VideoModel.listForApi( return res.json(getFormattedObjects(resultList.data, resultList.total))
req.query.start as number, }
req.query.count as number,
req.query.sort as VideoSortField, // Wrapper to video channel add that retry the async function if there is a database error
isNSFWHidden(res), // We need this because we run the transaction in SERIALIZABLE isolation that can fail
null, async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
false, const options = {
account.id arguments: [ req, res ],
) errorMessage: 'Cannot insert the video video channel with many retries.'
}
const videoChannel = await retryTransactionWrapper(addVideoChannel, options)
return res.json({
videoChannel: {
id: videoChannel.id
}
}).end()
}
async function addVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInfo: VideoChannelCreate = req.body
const account: AccountModel = res.locals.oauth.token.User.Account
const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => {
return createVideoChannel(videoChannelInfo, account, t)
})
setAsyncActorKeys(videoChannelCreated.Actor)
.catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err }))
logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
return videoChannelCreated
}
async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot update the video with many retries.'
}
await retryTransactionWrapper(updateVideoChannel, options)
return res.type('json').status(204).end()
}
async function updateVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance = res.locals.videoChannel as VideoChannelModel
const videoChannelFieldsSave = videoChannelInstance.toJSON()
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
try {
await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
await sendUpdateActor(videoChannelInstanceUpdated, t)
})
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
} catch (err) {
logger.debug('Cannot update the video channel.', { err })
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed!
resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave)
throw err
}
}
async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot remove the video channel with many retries.'
}
await retryTransactionWrapper(removeVideoChannel, options)
return res.type('json').status(204).end()
}
async function removeVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
return sequelizeTypescript.transaction(async t => {
await videoChannelInstance.destroy({ transaction: t })
logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
})
}
async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
return res.json(videoChannelWithVideos.toFormattedJSON())
}
async function listVideoChannelVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
const resultList = await VideoModel.listForApi({
start: req.query.start,
count: req.query.count,
sort: req.query.sort,
hideNSFW: isNSFWHidden(res),
withFiles: false,
videoChannelId: videoChannelInstance.id
})
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function listAccountVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
const account: AccountModel = res.locals.account
const resultList = await VideoModel.listForApi({
start: req.query.start,
count: req.query.count,
sort: req.query.sort,
hideNSFW: isNSFWHidden(res),
withFiles: false,
accountId: account.id
})
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }

View File

@ -7,6 +7,7 @@ import { usersRouter } from './users'
import { accountsRouter } from './accounts' import { accountsRouter } from './accounts'
import { videosRouter } from './videos' import { videosRouter } from './videos'
import { badRequest } from '../../helpers/express-utils' import { badRequest } from '../../helpers/express-utils'
import { videoChannelRouter } from './video-channel'
const apiRouter = express.Router() const apiRouter = express.Router()
@ -15,6 +16,7 @@ apiRouter.use('/oauth-clients', oauthClientsRouter)
apiRouter.use('/config', configRouter) apiRouter.use('/config', configRouter)
apiRouter.use('/users', usersRouter) apiRouter.use('/users', usersRouter)
apiRouter.use('/accounts', accountsRouter) apiRouter.use('/accounts', accountsRouter)
apiRouter.use('/video-channels', videoChannelRouter)
apiRouter.use('/videos', videosRouter) apiRouter.use('/videos', videosRouter)
apiRouter.use('/jobs', jobsRouter) apiRouter.use('/jobs', jobsRouter)
apiRouter.use('/ping', pong) apiRouter.use('/ping', pong)

View File

@ -0,0 +1,34 @@
import * as express from 'express'
import { getFormattedObjects } from '../../helpers/utils'
import {
asyncMiddleware,
paginationValidator,
setDefaultPagination,
setDefaultSort,
videoChannelsSortValidator
} from '../../middlewares'
import { VideoChannelModel } from '../../models/video/video-channel'
const videoChannelRouter = express.Router()
videoChannelRouter.get('/',
paginationValidator,
videoChannelsSortValidator,
setDefaultSort,
setDefaultPagination,
asyncMiddleware(listVideoChannels)
)
// ---------------------------------------------------------------------------
export {
videoChannelRouter
}
// ---------------------------------------------------------------------------
async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}

View File

@ -1,177 +0,0 @@
import * as express from 'express'
import { VideoChannelCreate, VideoChannelUpdate } from '../../../../shared'
import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { logger } from '../../../helpers/logger'
import { getFormattedObjects, resetSequelizeInstance } from '../../../helpers/utils'
import { sequelizeTypescript } from '../../../initializers'
import { setAsyncActorKeys } from '../../../lib/activitypub'
import { sendUpdateActor } from '../../../lib/activitypub/send'
import { createVideoChannel } from '../../../lib/video-channel'
import {
asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setDefaultPagination,
videoChannelsAddValidator, videoChannelsGetValidator, videoChannelsRemoveValidator, videoChannelsSortValidator,
videoChannelsUpdateValidator
} from '../../../middlewares'
import { AccountModel } from '../../../models/account/account'
import { VideoChannelModel } from '../../../models/video/video-channel'
const videoChannelRouter = express.Router()
videoChannelRouter.get('/channels',
paginationValidator,
videoChannelsSortValidator,
setDefaultSort,
setDefaultPagination,
asyncMiddleware(listVideoChannels)
)
videoChannelRouter.get('/accounts/:accountId/channels',
asyncMiddleware(listVideoAccountChannelsValidator),
asyncMiddleware(listVideoAccountChannels)
)
videoChannelRouter.post('/channels',
authenticate,
videoChannelsAddValidator,
asyncMiddleware(addVideoChannelRetryWrapper)
)
videoChannelRouter.put('/channels/:id',
authenticate,
asyncMiddleware(videoChannelsUpdateValidator),
updateVideoChannelRetryWrapper
)
videoChannelRouter.delete('/channels/:id',
authenticate,
asyncMiddleware(videoChannelsRemoveValidator),
asyncMiddleware(removeVideoChannelRetryWrapper)
)
videoChannelRouter.get('/channels/:id',
asyncMiddleware(videoChannelsGetValidator),
asyncMiddleware(getVideoChannel)
)
// ---------------------------------------------------------------------------
export {
videoChannelRouter
}
// ---------------------------------------------------------------------------
async function listVideoChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await VideoChannelModel.listForApi(req.query.start, req.query.count, req.query.sort)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function listVideoAccountChannels (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await VideoChannelModel.listByAccount(res.locals.account.id)
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
// Wrapper to video channel add that retry the async function if there is a database error
// We need this because we run the transaction in SERIALIZABLE isolation that can fail
async function addVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot insert the video video channel with many retries.'
}
const videoChannel = await retryTransactionWrapper(addVideoChannel, options)
return res.json({
videoChannel: {
id: videoChannel.id
}
}).end()
}
async function addVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInfo: VideoChannelCreate = req.body
const account: AccountModel = res.locals.oauth.token.User.Account
const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => {
return createVideoChannel(videoChannelInfo, account, t)
})
setAsyncActorKeys(videoChannelCreated.Actor)
.catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, { err }))
logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
return videoChannelCreated
}
async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot update the video with many retries.'
}
await retryTransactionWrapper(updateVideoChannel, options)
return res.type('json').status(204).end()
}
async function updateVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance = res.locals.videoChannel as VideoChannelModel
const videoChannelFieldsSave = videoChannelInstance.toJSON()
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
try {
await sequelizeTypescript.transaction(async t => {
const sequelizeOptions = {
transaction: t
}
if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
await sendUpdateActor(videoChannelInstanceUpdated, t)
})
logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
} catch (err) {
logger.debug('Cannot update the video channel.', { err })
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed!
resetSequelizeInstance(videoChannelInstance, videoChannelFieldsSave)
throw err
}
}
async function removeVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
const options = {
arguments: [ req, res ],
errorMessage: 'Cannot remove the video channel with many retries.'
}
await retryTransactionWrapper(removeVideoChannel, options)
return res.type('json').status(204).end()
}
async function removeVideoChannel (req: express.Request, res: express.Response) {
const videoChannelInstance: VideoChannelModel = res.locals.videoChannel
return sequelizeTypescript.transaction(async t => {
await videoChannelInstance.destroy({ transaction: t })
logger.info('Video channel with name %s and uuid %s deleted.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
})
}
async function getVideoChannel (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
return res.json(videoChannelWithVideos.toFormattedJSON())
}

View File

@ -42,7 +42,6 @@ import { VideoModel } from '../../../models/video/video'
import { VideoFileModel } from '../../../models/video/video-file' import { VideoFileModel } from '../../../models/video/video-file'
import { abuseVideoRouter } from './abuse' import { abuseVideoRouter } from './abuse'
import { blacklistRouter } from './blacklist' import { blacklistRouter } from './blacklist'
import { videoChannelRouter } from './channel'
import { videoCommentRouter } from './comment' import { videoCommentRouter } from './comment'
import { rateVideoRouter } from './rate' import { rateVideoRouter } from './rate'
import { VideoFilter } from '../../../../shared/models/videos/video-query.type' import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
@ -72,7 +71,6 @@ const reqVideoFileUpdate = createReqFiles(
videosRouter.use('/', abuseVideoRouter) videosRouter.use('/', abuseVideoRouter)
videosRouter.use('/', blacklistRouter) videosRouter.use('/', blacklistRouter)
videosRouter.use('/', rateVideoRouter) videosRouter.use('/', rateVideoRouter)
videosRouter.use('/', videoChannelRouter)
videosRouter.use('/', videoCommentRouter) videosRouter.use('/', videoCommentRouter)
videosRouter.get('/categories', listVideoCategories) videosRouter.get('/categories', listVideoCategories)
@ -397,13 +395,14 @@ async function getVideoDescription (req: express.Request, res: express.Response)
} }
async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) { async function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
const resultList = await VideoModel.listForApi( const resultList = await VideoModel.listForApi({
req.query.start as number, start: req.query.start,
req.query.count as number, count: req.query.count,
req.query.sort as VideoSortField, sort: req.query.sort,
isNSFWHidden(res), hideNSFW: isNSFWHidden(res),
req.query.filter as VideoFilter filter: req.query.filter as VideoFilter,
) withFiles: false
})
return res.json(getFormattedObjects(resultList.data, resultList.total)) return res.json(getFormattedObjects(resultList.data, resultList.total))
} }

View File

@ -33,15 +33,15 @@ async function generateFeed (req: express.Request, res: express.Response, next:
const account: AccountModel = res.locals.account const account: AccountModel = res.locals.account
const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list' const hideNSFW = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY === 'do_not_list'
const resultList = await VideoModel.listForApi( const resultList = await VideoModel.listForApi({
start, start,
FEEDS.COUNT, count: FEEDS.COUNT,
req.query.sort as VideoSortField, sort: req.query.sort,
hideNSFW, hideNSFW,
req.query.filter, filter: req.query.filter,
true, withFiles: true,
account ? account.id : null accountId: account ? account.id : null
) })
// Adding video items to the feed, one at a time // Adding video items to the feed, one at a time
resultList.data.forEach(video => { resultList.data.forEach(video => {

View File

@ -16,7 +16,7 @@ const followValidator = [
if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') { if (isTestInstance() === false && CONFIG.WEBSERVER.SCHEME === 'http') {
return res.status(400) return res.status(400)
.json({ .json({
error: 'Cannot follow non HTTPS web server.' error: 'Cannot follow on a non HTTPS web server.'
}) })
.end() .end()
} }

View File

@ -26,6 +26,7 @@ const listVideoAccountChannelsValidator = [
] ]
const videoChannelsAddValidator = [ const videoChannelsAddValidator = [
param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'), body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
@ -41,6 +42,7 @@ const videoChannelsAddValidator = [
const videoChannelsUpdateValidator = [ const videoChannelsUpdateValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'), body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
@ -49,6 +51,7 @@ const videoChannelsUpdateValidator = [
logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await isAccountIdExist(req.params.accountId, res)) return
if (!await isVideoChannelExist(req.params.id, res)) return if (!await isVideoChannelExist(req.params.id, res)) return
// We need to make additional checks // We need to make additional checks
@ -70,11 +73,13 @@ const videoChannelsUpdateValidator = [
const videoChannelsRemoveValidator = [ const videoChannelsRemoveValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
param('accountId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params }) logger.debug('Checking videoChannelsRemove parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
if (!await isAccountIdExist(req.params.accountId, res)) return
if (!await isVideoChannelExist(req.params.id, res)) return if (!await isVideoChannelExist(req.params.id, res)) return
// Check if the user who did the request is able to delete the video // Check if the user who did the request is able to delete the video
@ -87,11 +92,14 @@ const videoChannelsRemoveValidator = [
const videoChannelsGetValidator = [ const videoChannelsGetValidator = [
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
param('accountId').optional().custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid account id'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => { async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsGet parameters', { parameters: req.params }) logger.debug('Checking videoChannelsGet parameters', { parameters: req.params })
if (areValidationErrors(req, res)) return if (areValidationErrors(req, res)) return
// On some routes, accountId is optional (for example in the ActivityPub route)
if (req.params.accountId && !await isAccountIdExist(req.params.accountId, res)) return
if (!await isVideoChannelExist(req.params.id, res)) return if (!await isVideoChannelExist(req.params.id, res)) return
return next() return next()

View File

@ -95,7 +95,14 @@ enum ScopeNames {
} }
@Scopes({ @Scopes({
[ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number, hideNSFW: boolean, filter?: VideoFilter, withFiles?: boolean, accountId?: number) => { [ScopeNames.AVAILABLE_FOR_LIST]: (options: {
actorId: number,
hideNSFW: boolean,
filter?: VideoFilter,
withFiles?: boolean,
accountId?: number,
videoChannelId?: number
}) => {
const accountInclude = { const accountInclude = {
attributes: [ 'name' ], attributes: [ 'name' ],
model: AccountModel.unscoped(), model: AccountModel.unscoped(),
@ -106,7 +113,7 @@ enum ScopeNames {
attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ], attributes: [ 'preferredUsername', 'url', 'serverId', 'avatarId' ],
model: ActorModel.unscoped(), model: ActorModel.unscoped(),
required: true, required: true,
where: VideoModel.buildActorWhereWithFilter(filter), where: VideoModel.buildActorWhereWithFilter(options.filter),
include: [ include: [
{ {
attributes: [ 'host' ], attributes: [ 'host' ],
@ -122,6 +129,18 @@ enum ScopeNames {
] ]
} }
const videoChannelInclude = {
attributes: [ 'name', 'description' ],
model: VideoChannelModel.unscoped(),
required: true,
where: {},
include: [
accountInclude
]
}
// Force actorId to be a number to avoid SQL injections
const actorIdNumber = parseInt(options.actorId.toString(), 10)
const query: IFindOptions<VideoModel> = { const query: IFindOptions<VideoModel> = {
where: { where: {
id: { id: {
@ -132,32 +151,23 @@ enum ScopeNames {
'(' + '(' +
'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' + 'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' +
'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' + 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
'WHERE "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) + 'WHERE "actorFollow"."actorId" = ' + actorIdNumber +
' UNION ' + ' UNION ' +
'SELECT "video"."id" AS "id" FROM "video" ' + 'SELECT "video"."id" AS "id" FROM "video" ' +
'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' + 'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' +
'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' + 'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' +
'LEFT JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' + 'LEFT JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' +
'WHERE "actor"."serverId" IS NULL OR "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) + 'WHERE "actor"."serverId" IS NULL OR "actorFollow"."actorId" = ' + actorIdNumber +
')' ')'
) )
}, },
privacy: VideoPrivacy.PUBLIC privacy: VideoPrivacy.PUBLIC
}, },
include: [ include: [ videoChannelInclude ]
{
attributes: [ 'name', 'description' ],
model: VideoChannelModel.unscoped(),
required: true,
include: [
accountInclude
]
}
]
} }
if (withFiles === true) { if (options.withFiles === true) {
query.include.push({ query.include.push({
model: VideoFileModel.unscoped(), model: VideoFileModel.unscoped(),
required: true required: true
@ -165,13 +175,19 @@ enum ScopeNames {
} }
// Hide nsfw videos? // Hide nsfw videos?
if (hideNSFW === true) { if (options.hideNSFW === true) {
query.where['nsfw'] = false query.where['nsfw'] = false
} }
if (accountId) { if (options.accountId) {
accountInclude.where = { accountInclude.where = {
id: accountId id: options.accountId
}
}
if (options.videoChannelId) {
videoChannelInclude.where = {
id: options.videoChannelId
} }
} }
@ -697,23 +713,37 @@ export class VideoModel extends Model<VideoModel> {
}) })
} }
static async listForApi ( static async listForApi (options: {
start: number, start: number,
count: number, count: number,
sort: string, sort: string,
hideNSFW: boolean, hideNSFW: boolean,
withFiles: boolean,
filter?: VideoFilter, filter?: VideoFilter,
withFiles = false, accountId?: number,
accountId?: number videoChannelId?: number
) { }) {
const query = { const query = {
offset: start, offset: options.start,
limit: count, limit: options.count,
order: getSort(sort) order: getSort(options.sort)
} }
const serverActor = await getServerActor() const serverActor = await getServerActor()
return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW, filter, withFiles, accountId ] }) const scopes = {
method: [
ScopeNames.AVAILABLE_FOR_LIST, {
actorId: serverActor.id,
hideNSFW: options.hideNSFW,
filter: options.filter,
withFiles: options.withFiles,
accountId: options.accountId,
videoChannelId: options.videoChannelId
}
]
}
return VideoModel.scope(scopes)
.findAndCountAll(query) .findAndCountAll(query)
.then(({ rows, count }) => { .then(({ rows, count }) => {
return { return {
@ -750,8 +780,16 @@ export class VideoModel extends Model<VideoModel> {
} }
const serverActor = await getServerActor() const serverActor = await getServerActor()
const scopes = {
method: [
ScopeNames.AVAILABLE_FOR_LIST, {
actorId: serverActor.id,
hideNSFW
}
]
}
return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id, hideNSFW ] }) return VideoModel.scope(scopes)
.findAndCountAll(query) .findAndCountAll(query)
.then(({ rows, count }) => { .then(({ rows, count }) => {
return { return {

View File

@ -4,15 +4,29 @@ import * as chai from 'chai'
import { omit } from 'lodash' import { omit } from 'lodash'
import 'mocha' import 'mocha'
import { import {
createUser, deleteVideoChannel, flushTests, getAccountVideoChannelsList, getVideoChannelsList, immutableAssign, killallServers, createUser,
makeGetRequest, makePostBodyRequest, makePutBodyRequest, runServer, ServerInfo, setAccessTokensToServers, userLogin deleteVideoChannel,
flushTests,
getAccountVideoChannelsList,
getVideoChannelsList,
immutableAssign,
killallServers,
makeGetRequest,
makePostBodyRequest,
makePutBodyRequest,
runServer,
ServerInfo,
setAccessTokensToServers,
userLogin
} from '../../utils' } from '../../utils'
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params' import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
import { getAccountsList } from '../../utils/users/accounts'
const expect = chai.expect const expect = chai.expect
describe('Test videos API validator', function () { describe('Test videos API validator', function () {
const path = '/api/v1/videos/channels' const videoChannelPath = '/api/v1/video-channels'
const accountPath = '/api/v1/accounts/'
let server: ServerInfo let server: ServerInfo
let accessTokenUser: string let accessTokenUser: string
@ -37,15 +51,15 @@ describe('Test videos API validator', function () {
describe('When listing a video channels', function () { describe('When listing a video channels', function () {
it('Should fail with a bad start pagination', async function () { it('Should fail with a bad start pagination', async function () {
await checkBadStartPagination(server.url, path, server.accessToken) await checkBadStartPagination(server.url, videoChannelPath, server.accessToken)
}) })
it('Should fail with a bad count pagination', async function () { it('Should fail with a bad count pagination', async function () {
await checkBadCountPagination(server.url, path, server.accessToken) await checkBadCountPagination(server.url, videoChannelPath, server.accessToken)
}) })
it('Should fail with an incorrect sort', async function () { it('Should fail with an incorrect sort', async function () {
await checkBadSortPagination(server.url, path, server.accessToken) await checkBadSortPagination(server.url, videoChannelPath, server.accessToken)
}) })
}) })
@ -60,12 +74,20 @@ describe('Test videos API validator', function () {
}) })
describe('When adding a video channel', function () { describe('When adding a video channel', function () {
let path: string
const baseCorrectParams = { const baseCorrectParams = {
name: 'hello', name: 'hello',
description: 'super description', description: 'super description',
support: 'super support text' support: 'super support text'
} }
before(async function () {
const res = await getAccountsList(server.url)
const accountId = res.body.data[0].id
path = accountPath + accountId + '/video-channels'
})
it('Should fail with a non authenticated user', async function () { it('Should fail with a non authenticated user', async function () {
await makePostBodyRequest({ url: server.url, path, token: 'none', fields: baseCorrectParams, statusCodeExpected: 401 }) await makePostBodyRequest({ url: server.url, path, token: 'none', fields: baseCorrectParams, statusCodeExpected: 401 })
}) })
@ -107,22 +129,27 @@ describe('Test videos API validator', function () {
}) })
describe('When updating a video channel', function () { describe('When updating a video channel', function () {
let path: string
const baseCorrectParams = { const baseCorrectParams = {
name: 'hello', name: 'hello',
description: 'super description' description: 'super description'
} }
let videoChannelId
before(async function () { before(async function () {
const res = await getVideoChannelsList(server.url, 0, 1) const res1 = await getVideoChannelsList(server.url, 0, 1)
videoChannelId = res.body.data[0].id const videoChannelId = res1.body.data[0].id
const res2 = await getAccountsList(server.url)
const accountId = res2.body.data[0].id
path = accountPath + accountId + '/video-channels/' + videoChannelId
}) })
it('Should fail with a non authenticated user', async function () { it('Should fail with a non authenticated user', async function () {
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
path: path + '/' + videoChannelId, path,
token: 'hi', token: 'hi',
fields: baseCorrectParams, fields: baseCorrectParams,
statusCodeExpected: 401 statusCodeExpected: 401
@ -132,7 +159,7 @@ describe('Test videos API validator', function () {
it('Should fail with another authenticated user', async function () { it('Should fail with another authenticated user', async function () {
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
path: path + '/' + videoChannelId, path,
token: accessTokenUser, token: accessTokenUser,
fields: baseCorrectParams, fields: baseCorrectParams,
statusCodeExpected: 403 statusCodeExpected: 403
@ -141,23 +168,23 @@ describe('Test videos API validator', function () {
it('Should fail with a long name', async function () { it('Should fail with a long name', async function () {
const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) }) const fields = immutableAssign(baseCorrectParams, { name: 'super'.repeat(25) })
await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
}) })
it('Should fail with a long description', async function () { it('Should fail with a long description', async function () {
const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(60) }) const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(60) })
await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
}) })
it('Should fail with a long support text', async function () { it('Should fail with a long support text', async function () {
const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) }) const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
}) })
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
await makePutBodyRequest({ await makePutBodyRequest({
url: server.url, url: server.url,
path: path + '/' + videoChannelId, path,
token: server.accessToken, token: server.accessToken,
fields: baseCorrectParams, fields: baseCorrectParams,
statusCodeExpected: 204 statusCodeExpected: 204
@ -166,17 +193,23 @@ describe('Test videos API validator', function () {
}) })
describe('When getting a video channel', function () { describe('When getting a video channel', function () {
let basePath: string
let videoChannelId: number let videoChannelId: number
before(async function () { before(async function () {
const res = await getVideoChannelsList(server.url, 0, 1) const res1 = await getVideoChannelsList(server.url, 0, 1)
videoChannelId = res.body.data[0].id videoChannelId = res1.body.data[0].id
const res2 = await getAccountsList(server.url)
const accountId = res2.body.data[0].id
basePath = accountPath + accountId + '/video-channels'
}) })
it('Should return the list of the video channels with nothing', async function () { it('Should return the list of the video channels with nothing', async function () {
const res = await makeGetRequest({ const res = await makeGetRequest({
url: server.url, url: server.url,
path, path: basePath,
statusCodeExpected: 200 statusCodeExpected: 200
}) })
@ -186,7 +219,7 @@ describe('Test videos API validator', function () {
it('Should fail without a correct uuid', async function () { it('Should fail without a correct uuid', async function () {
await makeGetRequest({ await makeGetRequest({
url: server.url, url: server.url,
path: path + '/coucou', path: basePath + '/coucou',
statusCodeExpected: 400 statusCodeExpected: 400
}) })
}) })
@ -194,7 +227,7 @@ describe('Test videos API validator', function () {
it('Should return 404 with an incorrect video channel', async function () { it('Should return 404 with an incorrect video channel', async function () {
await makeGetRequest({ await makeGetRequest({
url: server.url, url: server.url,
path: path + '/4da6fde3-88f7-4d16-b119-108df5630b06', path: basePath + '/4da6fde3-88f7-4d16-b119-108df5630b06',
statusCodeExpected: 404 statusCodeExpected: 404
}) })
}) })
@ -202,7 +235,7 @@ describe('Test videos API validator', function () {
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
await makeGetRequest({ await makeGetRequest({
url: server.url, url: server.url,
path: path + '/' + videoChannelId, path: basePath + '/' + videoChannelId,
statusCodeExpected: 200 statusCodeExpected: 200
}) })
}) })
@ -210,33 +243,41 @@ describe('Test videos API validator', function () {
describe('When deleting a video channel', function () { describe('When deleting a video channel', function () {
let videoChannelId: number let videoChannelId: number
let accountId: number
before(async function () { before(async function () {
const res = await getVideoChannelsList(server.url, 0, 1) const res1 = await getVideoChannelsList(server.url, 0, 1)
videoChannelId = res.body.data[0].id videoChannelId = res1.body.data[0].id
const res2 = await getAccountsList(server.url)
accountId = res2.body.data[0].id
}) })
it('Should fail with a non authenticated user', async function () { it('Should fail with a non authenticated user', async function () {
await deleteVideoChannel(server.url, 'coucou', videoChannelId, 401) await deleteVideoChannel(server.url, 'coucou', accountId, videoChannelId, 401)
}) })
it('Should fail with another authenticated user', async function () { it('Should fail with another authenticated user', async function () {
await deleteVideoChannel(server.url, accessTokenUser, videoChannelId, 403) await deleteVideoChannel(server.url, accessTokenUser, accountId, videoChannelId, 403)
}) })
it('Should fail with an unknown id', async function () { it('Should fail with an unknown account id', async function () {
await deleteVideoChannel(server.url, server.accessToken, 454554, 404) await deleteVideoChannel(server.url, server.accessToken, 454554,videoChannelId, 404)
})
it('Should fail with an unknown video channel id', async function () {
await deleteVideoChannel(server.url, server.accessToken, accountId,454554, 404)
}) })
it('Should succeed with the correct parameters', async function () { it('Should succeed with the correct parameters', async function () {
await deleteVideoChannel(server.url, server.accessToken, videoChannelId) await deleteVideoChannel(server.url, server.accessToken, accountId, videoChannelId)
}) })
it('Should fail to delete the last user video channel', async function () { it('Should fail to delete the last user video channel', async function () {
const res = await getVideoChannelsList(server.url, 0, 1) const res = await getVideoChannelsList(server.url, 0, 1)
videoChannelId = res.body.data[0].id videoChannelId = res.body.data[0].id
await deleteVideoChannel(server.url, server.accessToken, videoChannelId, 409) await deleteVideoChannel(server.url, server.accessToken, accountId, videoChannelId, 409)
}) })
}) })

View File

@ -39,6 +39,7 @@ import {
getVideoCommentThreads, getVideoCommentThreads,
getVideoThreadComments getVideoThreadComments
} from '../../utils/videos/video-comments' } from '../../utils/videos/video-comments'
import { getAccountsList } from '../../utils/users/accounts'
const expect = chai.expect const expect = chai.expect
@ -46,6 +47,7 @@ describe('Test multiple servers', function () {
let servers: ServerInfo[] = [] let servers: ServerInfo[] = []
const toRemove = [] const toRemove = []
let videoUUID = '' let videoUUID = ''
let accountId: number
let videoChannelId: number let videoChannelId: number
before(async function () { before(async function () {
@ -56,13 +58,20 @@ describe('Test multiple servers', function () {
// Get the access tokens // Get the access tokens
await setAccessTokensToServers(servers) await setAccessTokensToServers(servers)
const videoChannel = { {
name: 'my channel', const res = await getAccountsList(servers[0].url)
description: 'super channel' accountId = res.body.data[0].id
}
{
const videoChannel = {
name: 'my channel',
description: 'super channel'
}
await addVideoChannel(servers[ 0 ].url, servers[ 0 ].accessToken, accountId, videoChannel)
const channelRes = await getVideoChannelsList(servers[ 0 ].url, 0, 1)
videoChannelId = channelRes.body.data[ 0 ].id
} }
await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
const channelRes = await getVideoChannelsList(servers[0].url, 0, 1)
videoChannelId = channelRes.body.data[0].id
// Server 1 and server 2 follow each other // Server 1 and server 2 follow each other
await doubleFollow(servers[0], servers[1]) await doubleFollow(servers[0], servers[1])

View File

@ -17,12 +17,14 @@ import {
setAccessTokensToServers, setAccessTokensToServers,
updateVideoChannel updateVideoChannel
} from '../../utils/index' } from '../../utils/index'
import { getAccountsList } from '../../utils/users/accounts'
const expect = chai.expect const expect = chai.expect
describe('Test video channels', function () { describe('Test video channels', function () {
let servers: ServerInfo[] let servers: ServerInfo[]
let userInfo: User let userInfo: User
let accountId: number
let videoChannelId: number let videoChannelId: number
before(async function () { before(async function () {
@ -35,6 +37,11 @@ describe('Test video channels', function () {
await setAccessTokensToServers(servers) await setAccessTokensToServers(servers)
await doubleFollow(servers[0], servers[1]) await doubleFollow(servers[0], servers[1])
{
const res = await getAccountsList(servers[0].url)
accountId = res.body.data[0].id
}
await wait(5000) await wait(5000)
}) })
@ -54,7 +61,7 @@ describe('Test video channels', function () {
description: 'super video channel description', description: 'super video channel description',
support: 'super video channel support text' support: 'super video channel support text'
} }
const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel) const res = await addVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannel)
videoChannelId = res.body.videoChannel.id videoChannelId = res.body.videoChannel.id
// The channel is 1 is propagated to servers 2 // The channel is 1 is propagated to servers 2
@ -120,7 +127,7 @@ describe('Test video channels', function () {
support: 'video channel support text updated' support: 'video channel support text updated'
} }
await updateVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId, videoChannelAttributes) await updateVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannelId, videoChannelAttributes)
await wait(3000) await wait(3000)
}) })
@ -139,7 +146,7 @@ describe('Test video channels', function () {
}) })
it('Should get video channel', async function () { it('Should get video channel', async function () {
const res = await getVideoChannel(servers[0].url, videoChannelId) const res = await getVideoChannel(servers[0].url, accountId, videoChannelId)
const videoChannel = res.body const videoChannel = res.body
expect(videoChannel.displayName).to.equal('video channel updated') expect(videoChannel.displayName).to.equal('video channel updated')
@ -148,7 +155,7 @@ describe('Test video channels', function () {
}) })
it('Should delete video channel', async function () { it('Should delete video channel', async function () {
await deleteVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId) await deleteVideoChannel(servers[0].url, servers[0].accessToken, accountId, videoChannelId)
}) })
it('Should have video channel deleted', async function () { it('Should have video channel deleted', async function () {

View File

@ -7,7 +7,7 @@ type VideoChannelAttributes = {
} }
function getVideoChannelsList (url: string, start: number, count: number, sort?: string) { function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
const path = '/api/v1/videos/channels' const path = '/api/v1/video-channels'
const req = request(url) const req = request(url)
.get(path) .get(path)
@ -22,7 +22,7 @@ function getVideoChannelsList (url: string, start: number, count: number, sort?:
} }
function getAccountVideoChannelsList (url: string, accountId: number | string, specialStatus = 200) { function getAccountVideoChannelsList (url: string, accountId: number | string, specialStatus = 200) {
const path = '/api/v1/videos/accounts/' + accountId + '/channels' const path = '/api/v1/accounts/' + accountId + '/video-channels'
return request(url) return request(url)
.get(path) .get(path)
@ -31,8 +31,14 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
} }
function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 200) { function addVideoChannel (
const path = '/api/v1/videos/channels' url: string,
token: string,
accountId: number,
videoChannelAttributesArg: VideoChannelAttributes,
expectedStatus = 200
) {
const path = '/api/v1/accounts/' + accountId + '/video-channels/'
// Default attributes // Default attributes
let attributes = { let attributes = {
@ -50,9 +56,16 @@ function addVideoChannel (url: string, token: string, videoChannelAttributesArg:
.expect(expectedStatus) .expect(expectedStatus)
} }
function updateVideoChannel (url: string, token: string, channelId: number, attributes: VideoChannelAttributes, expectedStatus = 204) { function updateVideoChannel (
url: string,
token: string,
accountId: number,
channelId: number,
attributes: VideoChannelAttributes,
expectedStatus = 204
) {
const body = {} const body = {}
const path = '/api/v1/videos/channels/' + channelId const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
if (attributes.name) body['name'] = attributes.name if (attributes.name) body['name'] = attributes.name
if (attributes.description) body['description'] = attributes.description if (attributes.description) body['description'] = attributes.description
@ -66,18 +79,18 @@ function updateVideoChannel (url: string, token: string, channelId: number, attr
.expect(expectedStatus) .expect(expectedStatus)
} }
function deleteVideoChannel (url: string, token: string, channelId: number, expectedStatus = 204) { function deleteVideoChannel (url: string, token: string, accountId: number, channelId: number, expectedStatus = 204) {
const path = '/api/v1/videos/channels/' const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
return request(url) return request(url)
.delete(path + channelId) .delete(path)
.set('Accept', 'application/json') .set('Accept', 'application/json')
.set('Authorization', 'Bearer ' + token) .set('Authorization', 'Bearer ' + token)
.expect(expectedStatus) .expect(expectedStatus)
} }
function getVideoChannel (url: string, channelId: number) { function getVideoChannel (url: string, accountId: number, channelId: number) {
const path = '/api/v1/videos/channels/' + channelId const path = '/api/v1/accounts/' + accountId + '/video-channels/' + channelId
return request(url) return request(url)
.get(path) .get(path)

File diff suppressed because it is too large Load Diff

View File

@ -91,7 +91,7 @@ paths:
in: path in: path
required: true required: true
type: string type: string
enum: ['xml', 'atom' 'json'] enum: [ 'xml', 'atom', 'json']
default: 'xml' default: 'xml'
description: 'The format expected (xml defaults to RSS 2.0, atom to ATOM 1.0 and json to JSON FEED 1.0' description: 'The format expected (xml defaults to RSS 2.0, atom to ATOM 1.0 and json to JSON FEED 1.0'
- name: accountId - name: accountId
@ -967,7 +967,7 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/VideoBlacklist' $ref: '#/definitions/VideoBlacklist'
/videos/channels: /video-channels:
get: get:
tags: tags:
- VideoChannel - VideoChannel
@ -998,83 +998,7 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/VideoChannel' $ref: '#/definitions/VideoChannel'
post: /accounts/{accountId}/video-channels:
security:
- OAuth2: [ ]
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: body
schema:
$ref: '#/definitions/VideoChannelInput'
responses:
'204':
description: successful operation
"/videos/channels/{id}":
get:
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: id
in: path
required: true
type: string
description: 'The video id '
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/VideoChannel'
put:
security:
- OAuth2: [ ]
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: id
in: path
required: true
type: string
description: 'The video id '
- in: body
name: body
schema:
$ref: '#/definitions/VideoChannelInput'
responses:
'204':
description: successful operation
delete:
security:
- OAuth2: [ ]
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: id
in: path
required: true
type: string
description: 'The video id '
responses:
'204':
description: successful operation
/videos/accounts/{accountId}/channels:
get: get:
tags: tags:
- VideoChannel - VideoChannel
@ -1095,6 +1019,102 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/VideoChannel' $ref: '#/definitions/VideoChannel'
post:
security:
- OAuth2: [ ]
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: accountId
in: path
required: true
type: string
description: 'The account id '
- in: body
name: body
schema:
$ref: '#/definitions/VideoChannelInput'
responses:
'204':
description: successful operation
"/account/{accountId}/video-channels/{id}":
get:
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: accountId
in: path
required: true
type: string
description: 'The account id '
- name: id
in: path
required: true
type: string
description: 'The video channel id '
responses:
'200':
description: successful operation
schema:
$ref: '#/definitions/VideoChannel'
put:
security:
- OAuth2: [ ]
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: accountId
in: path
required: true
type: string
description: 'The account id '
- name: id
in: path
required: true
type: string
description: 'The video channel id '
- in: body
name: body
schema:
$ref: '#/definitions/VideoChannelInput'
responses:
'204':
description: successful operation
delete:
security:
- OAuth2: [ ]
tags:
- VideoChannel
consumes:
- application/json
produces:
- application/json
parameters:
- name: accountId
in: path
required: true
type: string
description: 'The account id '
- name: id
in: path
required: true
type: string
description: 'The video channel id '
responses:
'204':
description: successful operation
"/videos/{videoId}/comment-threads": "/videos/{videoId}/comment-threads":
get: get:
tags: tags: