Server: Bulk update videos support field
This commit is contained in:
parent
9977c12838
commit
7d14d4d2ca
|
@ -19,7 +19,7 @@ import { VideoChannelModel } from '../../models/video/video-channel'
|
||||||
import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators'
|
import { videoChannelsNameWithHostValidator, videosSortValidator } from '../../middlewares/validators'
|
||||||
import { sendUpdateActor } from '../../lib/activitypub/send'
|
import { sendUpdateActor } from '../../lib/activitypub/send'
|
||||||
import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
|
import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
|
||||||
import { createVideoChannel } from '../../lib/video-channel'
|
import { createVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
|
||||||
import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
|
import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
|
||||||
import { setAsyncActorKeys } from '../../lib/activitypub'
|
import { setAsyncActorKeys } from '../../lib/activitypub'
|
||||||
import { AccountModel } from '../../models/account/account'
|
import { AccountModel } from '../../models/account/account'
|
||||||
|
@ -160,6 +160,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||||
const videoChannelFieldsSave = videoChannelInstance.toJSON()
|
const videoChannelFieldsSave = videoChannelInstance.toJSON()
|
||||||
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
|
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannelInstance.toFormattedJSON())
|
||||||
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
|
const videoChannelInfoToUpdate = req.body as VideoChannelUpdate
|
||||||
|
let doBulkVideoUpdate = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await sequelizeTypescript.transaction(async t => {
|
await sequelizeTypescript.transaction(async t => {
|
||||||
|
@ -167,9 +168,18 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||||
transaction: t
|
transaction: t
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.displayName)
|
if (videoChannelInfoToUpdate.displayName !== undefined) videoChannelInstance.name = videoChannelInfoToUpdate.displayName
|
||||||
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
|
if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.description = videoChannelInfoToUpdate.description
|
||||||
if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
|
|
||||||
|
if (videoChannelInfoToUpdate.support !== undefined) {
|
||||||
|
const oldSupportField = videoChannelInstance.support
|
||||||
|
videoChannelInstance.support = videoChannelInfoToUpdate.support
|
||||||
|
|
||||||
|
if (videoChannelInfoToUpdate.bulkVideosSupportUpdate === true && oldSupportField !== videoChannelInfoToUpdate.support) {
|
||||||
|
doBulkVideoUpdate = true
|
||||||
|
await VideoModel.bulkUpdateSupportField(videoChannelInstance, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
|
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
|
||||||
await sendUpdateActor(videoChannelInstanceUpdated, t)
|
await sendUpdateActor(videoChannelInstanceUpdated, t)
|
||||||
|
@ -179,6 +189,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||||
new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
|
new VideoChannelAuditView(videoChannelInstanceUpdated.toFormattedJSON()),
|
||||||
oldVideoChannelAuditKeys
|
oldVideoChannelAuditKeys
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
|
logger.info('Video channel %s updated.', videoChannelInstance.Actor.url)
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -192,7 +203,12 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
res.type('json').status(204).end()
|
||||||
|
|
||||||
|
// Don't process in a transaction, and after the response because it could be long
|
||||||
|
if (doBulkVideoUpdate) {
|
||||||
|
await federateAllVideosOfChannel(videoChannelInstance)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeVideoChannel (req: express.Request, res: express.Response) {
|
async function removeVideoChannel (req: express.Request, res: express.Response) {
|
||||||
|
|
|
@ -87,6 +87,7 @@ async function processCreateVideoComment (activity: ActivityCreate, byActor: Act
|
||||||
commentObject.inReplyTo,
|
commentObject.inReplyTo,
|
||||||
{ err }
|
{ err }
|
||||||
)
|
)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { comment, created } = await addVideoComment(video, commentObject.id)
|
const { comment, created } = await addVideoComment(video, commentObject.id)
|
||||||
|
|
|
@ -3,7 +3,8 @@ import * as uuidv4 from 'uuid/v4'
|
||||||
import { VideoChannelCreate } from '../../shared/models'
|
import { VideoChannelCreate } from '../../shared/models'
|
||||||
import { AccountModel } from '../models/account/account'
|
import { AccountModel } from '../models/account/account'
|
||||||
import { VideoChannelModel } from '../models/video/video-channel'
|
import { VideoChannelModel } from '../models/video/video-channel'
|
||||||
import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub'
|
import { buildActorInstance, federateVideoIfNeeded, getVideoChannelActivityPubUrl } from './activitypub'
|
||||||
|
import { VideoModel } from '../models/video/video'
|
||||||
|
|
||||||
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
|
async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) {
|
||||||
const uuid = uuidv4()
|
const uuid = uuidv4()
|
||||||
|
@ -33,8 +34,19 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
|
||||||
return videoChannelCreated
|
return videoChannelCreated
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function federateAllVideosOfChannel (videoChannel: VideoChannelModel) {
|
||||||
|
const videoIds = await VideoModel.getAllIdsFromChannel(videoChannel)
|
||||||
|
|
||||||
|
for (const videoId of videoIds) {
|
||||||
|
const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoId)
|
||||||
|
|
||||||
|
await federateVideoIfNeeded(video, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
createVideoChannel
|
createVideoChannel,
|
||||||
|
federateAllVideosOfChannel
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { VideoChannelModel } from '../../../models/video/video-channel'
|
||||||
import { areValidationErrors } from '../utils'
|
import { areValidationErrors } from '../utils'
|
||||||
import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
|
import { isActorPreferredUsernameValid } from '../../../helpers/custom-validators/activitypub/actor'
|
||||||
import { ActorModel } from '../../../models/activitypub/actor'
|
import { ActorModel } from '../../../models/activitypub/actor'
|
||||||
|
import { isBooleanValid } from '../../../helpers/custom-validators/misc'
|
||||||
|
|
||||||
const videoChannelsAddValidator = [
|
const videoChannelsAddValidator = [
|
||||||
body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
|
body('name').custom(isActorPreferredUsernameValid).withMessage('Should have a valid channel name'),
|
||||||
|
@ -40,9 +41,18 @@ const videoChannelsAddValidator = [
|
||||||
|
|
||||||
const videoChannelsUpdateValidator = [
|
const videoChannelsUpdateValidator = [
|
||||||
param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
|
param('nameWithHost').exists().withMessage('Should have an video channel name with host'),
|
||||||
body('displayName').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
|
body('displayName')
|
||||||
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
|
.optional()
|
||||||
body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
|
.custom(isVideoChannelNameValid).withMessage('Should have a valid display name'),
|
||||||
|
body('description')
|
||||||
|
.optional()
|
||||||
|
.custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
|
||||||
|
body('support')
|
||||||
|
.optional()
|
||||||
|
.custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
|
||||||
|
body('bulkVideosSupportUpdate')
|
||||||
|
.optional()
|
||||||
|
.custom(isBooleanValid).withMessage('Should have a valid bulkVideosSupportUpdate boolean field'),
|
||||||
|
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
|
logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
|
||||||
|
|
|
@ -1515,6 +1515,29 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
.then(results => results.length === 1)
|
.then(results => results.length === 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bulkUpdateSupportField (videoChannel: VideoChannelModel, t: Transaction) {
|
||||||
|
const options = {
|
||||||
|
where: {
|
||||||
|
channelId: videoChannel.id
|
||||||
|
},
|
||||||
|
transaction: t
|
||||||
|
}
|
||||||
|
|
||||||
|
return VideoModel.update({ support: videoChannel.support }, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAllIdsFromChannel (videoChannel: VideoChannelModel) {
|
||||||
|
const query = {
|
||||||
|
attributes: [ 'id' ],
|
||||||
|
where: {
|
||||||
|
channelId: videoChannel.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VideoModel.findAll(query)
|
||||||
|
.then(videos => videos.map(v => v.id))
|
||||||
|
}
|
||||||
|
|
||||||
// threshold corresponds to how many video the field should have to be returned
|
// threshold corresponds to how many video the field should have to be returned
|
||||||
static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) {
|
static async getRandomFieldSamples (field: 'category' | 'channelId', threshold: number, count: number) {
|
||||||
const serverActor = await getServerActor()
|
const serverActor = await getServerActor()
|
||||||
|
|
|
@ -24,6 +24,7 @@ import {
|
||||||
checkBadStartPagination
|
checkBadStartPagination
|
||||||
} from '../../../../shared/extra-utils/requests/check-api-params'
|
} from '../../../../shared/extra-utils/requests/check-api-params'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
import { VideoChannelUpdate } from '../../../../shared/models/videos'
|
||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
|
@ -169,9 +170,11 @@ describe('Test video channels API validator', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('When updating a video channel', function () {
|
describe('When updating a video channel', function () {
|
||||||
const baseCorrectParams = {
|
const baseCorrectParams: VideoChannelUpdate = {
|
||||||
displayName: 'hello',
|
displayName: 'hello',
|
||||||
description: 'super description'
|
description: 'super description',
|
||||||
|
support: 'toto',
|
||||||
|
bulkVideosSupportUpdate: false
|
||||||
}
|
}
|
||||||
let path: string
|
let path: string
|
||||||
|
|
||||||
|
@ -214,6 +217,11 @@ describe('Test video channels API validator', function () {
|
||||||
await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
await makePutBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should fail with a bad bulkVideosSupportUpdate field', async function () {
|
||||||
|
const fields = immutableAssign(baseCorrectParams, { bulkVideosSupportUpdate: 'super' })
|
||||||
|
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,
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
|
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import { User, Video, VideoChannel } from '../../../../shared/index'
|
import { User, Video, VideoChannel, VideoDetails } from '../../../../shared/index'
|
||||||
import {
|
import {
|
||||||
cleanupTests,
|
cleanupTests,
|
||||||
createUser,
|
createUser,
|
||||||
doubleFollow,
|
doubleFollow,
|
||||||
flushAndRunMultipleServers,
|
flushAndRunMultipleServers, getVideo,
|
||||||
getVideoChannelVideos,
|
getVideoChannelVideos,
|
||||||
testImage,
|
testImage,
|
||||||
updateVideo,
|
updateVideo,
|
||||||
|
@ -79,7 +79,8 @@ describe('Test video channels', function () {
|
||||||
|
|
||||||
// The channel is 1 is propagated to servers 2
|
// The channel is 1 is propagated to servers 2
|
||||||
{
|
{
|
||||||
const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, { name: 'my video name', channelId: secondVideoChannelId })
|
const videoAttributesArg = { name: 'my video name', channelId: secondVideoChannelId, support: 'video support field' }
|
||||||
|
const res = await uploadVideo(servers[ 0 ].url, servers[ 0 ].accessToken, videoAttributesArg)
|
||||||
videoUUID = res.body.video.uuid
|
videoUUID = res.body.video.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,12 +202,12 @@ describe('Test video channels', function () {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should update video channel', async function () {
|
it('Should update video channel', async function () {
|
||||||
this.timeout(5000)
|
this.timeout(15000)
|
||||||
|
|
||||||
const videoChannelAttributes = {
|
const videoChannelAttributes = {
|
||||||
displayName: 'video channel updated',
|
displayName: 'video channel updated',
|
||||||
description: 'video channel description updated',
|
description: 'video channel description updated',
|
||||||
support: 'video channel support text updated'
|
support: 'support updated'
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
|
await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
|
||||||
|
@ -224,7 +225,36 @@ describe('Test video channels', function () {
|
||||||
expect(res.body.data[0].name).to.equal('second_video_channel')
|
expect(res.body.data[0].name).to.equal('second_video_channel')
|
||||||
expect(res.body.data[0].displayName).to.equal('video channel updated')
|
expect(res.body.data[0].displayName).to.equal('video channel updated')
|
||||||
expect(res.body.data[0].description).to.equal('video channel description updated')
|
expect(res.body.data[0].description).to.equal('video channel description updated')
|
||||||
expect(res.body.data[0].support).to.equal('video channel support text updated')
|
expect(res.body.data[0].support).to.equal('support updated')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should not have updated the video support field', async function () {
|
||||||
|
for (const server of servers) {
|
||||||
|
const res = await getVideo(server.url, videoUUID)
|
||||||
|
const video: VideoDetails = res.body
|
||||||
|
|
||||||
|
expect(video.support).to.equal('video support field')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should update the channel support field and update videos too', async function () {
|
||||||
|
this.timeout(35000)
|
||||||
|
|
||||||
|
const videoChannelAttributes = {
|
||||||
|
support: 'video channel support text updated',
|
||||||
|
bulkVideosSupportUpdate: true
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateVideoChannel(servers[0].url, servers[0].accessToken, 'second_video_channel', videoChannelAttributes)
|
||||||
|
|
||||||
|
await waitJobs(servers)
|
||||||
|
|
||||||
|
for (const server of servers) {
|
||||||
|
const res = await getVideo(server.url, videoUUID)
|
||||||
|
const video: VideoDetails = res.body
|
||||||
|
|
||||||
|
expect(video.support).to.equal(videoChannelAttributes.support)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -74,12 +74,13 @@ function updateVideoChannel (
|
||||||
attributes: VideoChannelUpdate,
|
attributes: VideoChannelUpdate,
|
||||||
expectedStatus = 204
|
expectedStatus = 204
|
||||||
) {
|
) {
|
||||||
const body = {}
|
const body: any = {}
|
||||||
const path = '/api/v1/video-channels/' + channelName
|
const path = '/api/v1/video-channels/' + channelName
|
||||||
|
|
||||||
if (attributes.displayName) body['displayName'] = attributes.displayName
|
if (attributes.displayName) body.displayName = attributes.displayName
|
||||||
if (attributes.description) body['description'] = attributes.description
|
if (attributes.description) body.description = attributes.description
|
||||||
if (attributes.support) body['support'] = attributes.support
|
if (attributes.support) body.support = attributes.support
|
||||||
|
if (attributes.bulkVideosSupportUpdate) body.bulkVideosSupportUpdate = attributes.bulkVideosSupportUpdate
|
||||||
|
|
||||||
return request(url)
|
return request(url)
|
||||||
.put(path)
|
.put(path)
|
||||||
|
|
|
@ -355,6 +355,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
|
||||||
.set('Accept', 'application/json')
|
.set('Accept', 'application/json')
|
||||||
.set('Authorization', 'Bearer ' + accessToken)
|
.set('Authorization', 'Bearer ' + accessToken)
|
||||||
.field('name', attributes.name)
|
.field('name', attributes.name)
|
||||||
|
.field('support', attributes.support)
|
||||||
.field('nsfw', JSON.stringify(attributes.nsfw))
|
.field('nsfw', JSON.stringify(attributes.nsfw))
|
||||||
.field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
|
.field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
|
||||||
.field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
|
.field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
export interface VideoChannelUpdate {
|
export interface VideoChannelUpdate {
|
||||||
displayName: string
|
displayName?: string
|
||||||
description?: string
|
description?: string
|
||||||
support?: string
|
support?: string
|
||||||
|
|
||||||
|
bulkVideosSupportUpdate?: boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -1322,7 +1322,10 @@ paths:
|
||||||
'204':
|
'204':
|
||||||
$ref: '#/paths/~1users~1me/put/responses/204'
|
$ref: '#/paths/~1users~1me/put/responses/204'
|
||||||
requestBody:
|
requestBody:
|
||||||
$ref: '#/components/requestBodies/VideoChannelInput'
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VideoChannelCreate'
|
||||||
'/video-channels/{channelHandle}':
|
'/video-channels/{channelHandle}':
|
||||||
get:
|
get:
|
||||||
summary: Get a video channel by its id
|
summary: Get a video channel by its id
|
||||||
|
@ -1349,7 +1352,10 @@ paths:
|
||||||
'204':
|
'204':
|
||||||
$ref: '#/paths/~1users~1me/put/responses/204'
|
$ref: '#/paths/~1users~1me/put/responses/204'
|
||||||
requestBody:
|
requestBody:
|
||||||
$ref: '#/components/requestBodies/VideoChannelInput'
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/VideoChannelUpdate'
|
||||||
delete:
|
delete:
|
||||||
summary: Delete a video channel by its id
|
summary: Delete a video channel by its id
|
||||||
security:
|
security:
|
||||||
|
@ -1775,12 +1781,6 @@ components:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
requestBodies:
|
|
||||||
VideoChannelInput:
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/VideoChannelInput'
|
|
||||||
securitySchemes:
|
securitySchemes:
|
||||||
OAuth2:
|
OAuth2:
|
||||||
description: >
|
description: >
|
||||||
|
@ -2294,10 +2294,28 @@ components:
|
||||||
- username
|
- username
|
||||||
- password
|
- password
|
||||||
- email
|
- email
|
||||||
VideoChannelInput:
|
VideoChannelCreate:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
|
support:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- displayName
|
||||||
|
VideoChannelUpdate:
|
||||||
|
properties:
|
||||||
|
displayName:
|
||||||
|
type: string
|
||||||
|
description:
|
||||||
|
type: string
|
||||||
|
support:
|
||||||
|
type: string
|
||||||
|
bulkVideosSupportUpdate:
|
||||||
|
type: boolean
|
||||||
|
description: 'Update all videos support field of this channel'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue