url: string
name: string
displayName: string
+ description: string
host: string
followingCount: number
followersCount: number
languageLabel: string
language: number
description: string
+ support: string
duration: number
durationLabel: string
id: number
import { createReqFiles, getFormattedObjects } from '../../helpers/utils'
import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers'
import { updateActorAvatarInstance } from '../../lib/activitypub'
-import { sendUpdateUser } from '../../lib/activitypub/send'
+import { sendUpdateActor } from '../../lib/activitypub/send'
import { Emailer } from '../../lib/emailer'
import { Redis } from '../../lib/redis'
import { createUserAccountAndChannel } from '../../lib/user'
async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) {
const body: UserUpdateMe = req.body
- const user = res.locals.oauth.token.user
+ const user: UserModel = res.locals.oauth.token.user
if (body.password !== undefined) user.password = body.password
if (body.email !== undefined) user.email = body.email
if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW
if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
- await user.save()
- await sendUpdateUser(user, undefined)
+ await sequelizeTypescript.transaction(async t => {
+ await user.save({ transaction: t })
+
+ if (body.description !== undefined) user.Account.description = body.description
+ await user.Account.save({ transaction: t })
+
+ await sendUpdateActor(user.Account, t)
+ })
return res.sendStatus(204)
}
const updatedActor = await updateActorAvatarInstance(actor, avatarName, t)
await updatedActor.save({ transaction: t })
- await sendUpdateUser(user, t)
+ await sendUpdateActor(user.Account, t)
return updatedActor.Avatar
})
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,
errorMessage: 'Cannot insert the video video channel with many retries.'
}
- await retryTransactionWrapper(addVideoChannel, options)
-
- // TODO : include Location of the new video channel -> 201
- return res.type('json').status(204).end()
+ 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 = await sequelizeTypescript.transaction(async t => {
+ 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) {
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)
- await videoChannelInstance.save(sequelizeOptions)
-
- // TODO
- // await sendUpdateVideoChannel(videoChannelInstanceUpdated, t)
+ 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)
commentsEnabled: videoInfo.commentsEnabled,
nsfw: videoInfo.nsfw,
description: videoInfo.description,
+ support: videoInfo.support,
privacy: videoInfo.privacy,
duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
channelId: res.locals.videoChannel.id
if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10))
+ if (videoInfoToUpdate.support !== undefined) videoInstance.set('support', videoInfoToUpdate.support)
if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.set('commentsEnabled', videoInfoToUpdate.commentsEnabled)
'language': 'http://schema.org/inLanguage',
'views': 'http://schema.org/Number',
'size': 'http://schema.org/Number',
- 'commentsEnabled': 'http://schema.org/Boolean'
+ 'commentsEnabled': 'http://schema.org/Boolean',
+ 'support': 'http://schema.org/Text'
},
{
likes: {
import 'express-validator'
import * as validator from 'validator'
import { AccountModel } from '../../models/account/account'
-import { isUserUsernameValid } from './users'
+import { isUserDescriptionValid, isUserUsernameValid } from './users'
function isAccountNameValid (value: string) {
return isUserUsernameValid(value)
}
+function isAccountDescriptionValid (value: string) {
+ return isUserDescriptionValid(value)
+}
+
function isAccountIdExist (id: number | string, res: Response) {
let promise: Bluebird<AccountModel>
export {
isAccountIdExist,
isLocalAccountNameExist,
+ isAccountDescriptionValid,
isAccountNameValid
}
return exists(value) && validator.matches(value, new RegExp(`^[a-z0-9._]{${min},${max}}$`))
}
+function isUserDescriptionValid (value: string) {
+ return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION))
+}
+
function isBoolean (value: any) {
return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
}
isUserUsernameValid,
isUserDisplayNSFWValid,
isUserAutoPlayVideoValid,
+ isUserDescriptionValid,
isAvatarFile
}
return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME)
}
+function isVideoChannelSupportValid (value: string) {
+ return value === null || (exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.SUPPORT))
+}
+
async function isVideoChannelExist (id: string, res: express.Response) {
let videoChannel: VideoChannelModel
if (validator.isInt(id)) {
export {
isVideoChannelDescriptionValid,
isVideoChannelNameValid,
+ isVideoChannelSupportValid,
isVideoChannelExist
}
return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
}
+function isVideoSupportValid (value: string) {
+ return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
+}
+
function isVideoNameValid (value: string) {
return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
}
isVideoFileResolutionValid,
isVideoFileSizeValid,
isVideoExist,
- isVideoImage
+ isVideoImage,
+ isVideoSupportValid
}
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 190
+const LAST_MIGRATION_VERSION = 195
// ---------------------------------------------------------------------------
USERS: {
USERNAME: { min: 3, max: 20 }, // Length
PASSWORD: { min: 6, max: 255 }, // Length
+ DESCRIPTION: { min: 3, max: 250 }, // Length
VIDEO_QUOTA: { min: -1 }
},
VIDEO_ABUSES: {
VIDEO_CHANNELS: {
NAME: { min: 3, max: 120 }, // Length
DESCRIPTION: { min: 3, max: 250 }, // Length
+ SUPPORT: { min: 3, max: 300 }, // Length
URL: { min: 3, max: 2000 } // Length
},
VIDEOS: {
NAME: { min: 3, max: 120 }, // Length
TRUNCATED_DESCRIPTION: { min: 3, max: 250 }, // Length
- DESCRIPTION: { min: 3, max: 3000 }, // Length
+ DESCRIPTION: { min: 3, max: 10000 }, // Length
+ SUPPORT: { min: 3, max: 300 }, // Length
IMAGE: {
EXTNAME: [ '.jpg', '.jpeg' ],
FILE_SIZE: {
--- /dev/null
+import * as Sequelize from 'sequelize'
+import { CONSTRAINTS_FIELDS } from '../index'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction,
+ queryInterface: Sequelize.QueryInterface,
+ sequelize: Sequelize.Sequelize
+}): Promise<void> {
+ {
+ const data = {
+ type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max),
+ allowNull: true,
+ defaultValue: null
+ }
+ await utils.queryInterface.addColumn('video', 'support', data)
+ }
+
+ {
+ const data = {
+ type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max),
+ allowNull: true,
+ defaultValue: null
+ }
+ await utils.queryInterface.addColumn('videoChannel', 'support', data)
+ }
+
+ {
+ const data = {
+ type: Sequelize.STRING(CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max),
+ allowNull: true,
+ defaultValue: null
+ }
+ await utils.queryInterface.addColumn('account', 'description', data)
+ }
+
+ {
+ const data = {
+ type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max),
+ allowNull: true,
+ defaultValue: null
+ }
+ await utils.queryInterface.changeColumn('video', 'description', data)
+ }
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
})
if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
- const account = await saveAccount(actorCreated, result, t)
- actorCreated.Account = account
+ actorCreated.Account = await saveAccount(actorCreated, result, t)
actorCreated.Account.Actor = actorCreated
} else if (actorCreated.type === 'Group') { // Video channel
- const videoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t)
- actorCreated.VideoChannel = videoChannel
+ actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t)
actorCreated.VideoChannel.Actor = actorCreated
}
actor: ActorModel
name: string
summary: string
+ support?: string
avatarName?: string
attributedTo: ActivityPubAttributedTo[]
}
name,
avatarName,
summary: actorJSON.summary,
+ support: actorJSON.support,
attributedTo: actorJSON.attributedTo
}
}
const [ accountCreated ] = await AccountModel.findOrCreate({
defaults: {
name: result.name,
+ description: result.summary,
actorId: actor.id
},
where: {
defaults: {
name: result.name,
description: result.summary,
+ support: result.support,
actorId: actor.id,
accountId: ownerActor.Account.id
},
await actor.save({ transaction: t })
actor.Account.set('name', result.name)
+ actor.Account.set('description', result.summary)
await actor.Account.save({ transaction: t })
} else if (actor.VideoChannel) {
await actor.save({ transaction: t })
actor.VideoChannel.set('name', result.name)
+ actor.VideoChannel.set('description', result.summary)
+ actor.VideoChannel.set('support', result.support)
await actor.VideoChannel.save({ transaction: t })
}
import { AccountModel } from '../../../models/account/account'
import { ActorModel } from '../../../models/activitypub/actor'
import { TagModel } from '../../../models/video/tag'
+import { VideoChannelModel } from '../../../models/video/video-channel'
import { VideoFileModel } from '../../../models/video/video-file'
import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
import {
- generateThumbnailFromUrl, getOrCreateAccountAndVideoAndChannel, videoActivityObjectToDBAttributes,
+ generateThumbnailFromUrl,
+ getOrCreateAccountAndVideoAndChannel,
+ videoActivityObjectToDBAttributes,
videoFileActivityUrlToDBAttributes
} from '../videos'
async function processUpdateActivity (activity: ActivityUpdate) {
const actor = await getOrCreateActorAndServerAndModel(activity.actor)
+ const objectType = activity.object.type
- if (activity.object.type === 'Video') {
+ if (objectType === 'Video') {
return processUpdateVideo(actor, activity)
- } else if (activity.object.type === 'Person') {
- return processUpdateAccount(actor, activity)
+ } else if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
+ return processUpdateActor(actor, activity)
}
return
videoInstance.set('licence', videoData.licence)
videoInstance.set('language', videoData.language)
videoInstance.set('description', videoData.description)
+ videoInstance.set('support', videoData.support)
videoInstance.set('nsfw', videoData.nsfw)
videoInstance.set('commentsEnabled', videoData.commentsEnabled)
videoInstance.set('duration', videoData.duration)
}
}
-function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) {
+function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) {
const options = {
arguments: [ actor, activity ],
- errorMessage: 'Cannot update the remote account with many retries'
+ errorMessage: 'Cannot update the remote actor with many retries'
}
- return retryTransactionWrapper(updateRemoteAccount, options)
+ return retryTransactionWrapper(updateRemoteActor, options)
}
-async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) {
- const accountAttributesToUpdate = activity.object as ActivityPubActor
+async function updateRemoteActor (actor: ActorModel, activity: ActivityUpdate) {
+ const actorAttributesToUpdate = activity.object as ActivityPubActor
- logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid)
- let accountInstance: AccountModel
+ logger.debug('Updating remote account "%s".', actorAttributesToUpdate.uuid)
+ let accountOrChannelInstance: AccountModel | VideoChannelModel
let actorFieldsSave: object
- let accountFieldsSave: object
+ let accountOrChannelFieldsSave: object
// Fetch icon?
- const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate)
+ const avatarName = await fetchAvatarIfExists(actorAttributesToUpdate)
try {
await sequelizeTypescript.transaction(async t => {
actorFieldsSave = actor.toJSON()
- accountInstance = actor.Account
- accountFieldsSave = actor.Account.toJSON()
- await updateActorInstance(actor, accountAttributesToUpdate)
+ if (actorAttributesToUpdate.type === 'Group') accountOrChannelInstance = actor.VideoChannel
+ else accountOrChannelInstance = actor.Account
+
+ accountOrChannelFieldsSave = accountOrChannelInstance.toJSON()
+
+ await updateActorInstance(actor, actorAttributesToUpdate)
if (avatarName !== undefined) {
await updateActorAvatarInstance(actor, avatarName, t)
await actor.save({ transaction: t })
- actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername)
- await actor.Account.save({ transaction: t })
+ accountOrChannelInstance.set('name', actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername)
+ accountOrChannelInstance.set('description', actorAttributesToUpdate.summary)
+ accountOrChannelInstance.set('support', actorAttributesToUpdate.support)
+ await accountOrChannelInstance.save({ transaction: t })
})
- logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid)
+ logger.info('Remote account with uuid %s updated', actorAttributesToUpdate.uuid)
} catch (err) {
if (actor !== undefined && actorFieldsSave !== undefined) {
resetSequelizeInstance(actor, actorFieldsSave)
}
- if (accountInstance !== undefined && accountFieldsSave !== undefined) {
- resetSequelizeInstance(accountInstance, accountFieldsSave)
+ if (accountOrChannelInstance !== undefined && accountOrChannelFieldsSave !== undefined) {
+ resetSequelizeInstance(accountOrChannelInstance, accountOrChannelFieldsSave)
}
// This is just a debug because we will retry the insert
import { Transaction } from 'sequelize'
import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
import { VideoPrivacy } from '../../../../shared/models/videos'
-import { UserModel } from '../../../models/account/user'
+import { AccountModel } from '../../../models/account/account'
import { ActorModel } from '../../../models/activitypub/actor'
import { VideoModel } from '../../../models/video/video'
+import { VideoChannelModel } from '../../../models/video/video-channel'
import { VideoShareModel } from '../../../models/video/video-share'
import { getUpdateActivityPubUrl } from '../url'
import { audiencify, broadcastToFollowers, getAudience } from './misc'
return broadcastToFollowers(data, byActor, actorsInvolved, t)
}
-async function sendUpdateUser (user: UserModel, t: Transaction) {
- const byActor = user.Account.Actor
+async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) {
+ const byActor = accountOrChannel.Actor
const url = getUpdateActivityPubUrl(byActor.url, byActor.updatedAt.toISOString())
- const accountObject = user.Account.toActivityPubObject()
+ const accountOrChannelObject = accountOrChannel.toActivityPubObject()
const audience = await getAudience(byActor, t)
- const data = await updateActivityData(url, byActor, accountObject, t, audience)
+ const data = await updateActivityData(url, byActor, accountOrChannelObject, t, audience)
+
+ let actorsInvolved: ActorModel[]
+ if (accountOrChannel instanceof AccountModel) {
+ // Actors that shared my videos are involved too
+ actorsInvolved = await VideoShareModel.loadActorsByVideoOwner(byActor.id, t)
+ } else {
+ // Actors that shared videos of my channel are involved too
+ actorsInvolved = await VideoShareModel.loadActorsByVideoChannel(accountOrChannel.id, t)
+ }
- const actorsInvolved = await VideoShareModel.loadActorsByVideoOwner(byActor.id, t)
actorsInvolved.push(byActor)
return broadcastToFollowers(data, byActor, actorsInvolved, t)
// ---------------------------------------------------------------------------
export {
- sendUpdateUser,
+ sendUpdateActor,
sendUpdateVideo
}
description = videoObject.content
}
+ let support = null
+ if (videoObject.support) {
+ support = videoObject.support
+ }
+
return {
name: videoObject.name,
uuid: videoObject.uuid,
licence,
language,
description,
+ support,
nsfw: videoObject.sensitive,
commentsEnabled: videoObject.commentsEnabled,
channelId: videoChannel.id,
const videoChannelData = {
name: videoChannelInfo.name,
description: videoChannelInfo.description,
+ support: videoChannelInfo.support,
accountId: account.id,
actorId: actorInstanceCreated.id
}
import {
isAvatarFile,
isUserAutoPlayVideoValid,
+ isUserDescriptionValid,
isUserDisplayNSFWValid,
isUserPasswordValid,
isUserRoleValid,
]
const usersUpdateMeValidator = [
+ body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
body('displayNSFW').optional().custom(isUserDisplayNSFWValid).withMessage('Should have a valid display Not Safe For Work attribute'),
import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
import {
isVideoChannelDescriptionValid, isVideoChannelExist,
- isVideoChannelNameValid
+ isVideoChannelNameValid, isVideoChannelSupportValid
} from '../../helpers/custom-validators/video-channels'
import { logger } from '../../helpers/logger'
import { UserModel } from '../../models/account/user'
const videoChannelsAddValidator = [
body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
- body('description').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'),
(req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body })
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
+ body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
isVideoLicenceValid,
isVideoNameValid,
isVideoPrivacyValid,
- isVideoRatingTypeValid,
+ isVideoRatingTypeValid, isVideoSupportValid,
isVideoTagsValid
} from '../../helpers/custom-validators/videos'
import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
+ body('support').optional().custom(isVideoSupportValid).withMessage('Should have a valid support text'),
body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'),
body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
body('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
+ body('support').optional().custom(isVideoSupportValid).withMessage('Should have a valid support text'),
body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
import * as Sequelize from 'sequelize'
import {
- AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table,
+ AllowNull,
+ BeforeDestroy,
+ BelongsTo,
+ Column,
+ CreatedAt,
+ Default,
+ DefaultScope,
+ ForeignKey,
+ HasMany,
+ Is,
+ Model,
+ Table,
UpdatedAt
} from 'sequelize-typescript'
import { Account } from '../../../shared/models/actors'
+import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
import { logger } from '../../helpers/logger'
import { sendDeleteActor } from '../../lib/activitypub/send'
import { ActorModel } from '../activitypub/actor'
import { ApplicationModel } from '../application/application'
import { AvatarModel } from '../avatar/avatar'
import { ServerModel } from '../server/server'
-import { getSort } from '../utils'
+import { getSort, throwIfNotValid } from '../utils'
import { VideoChannelModel } from '../video/video-channel'
import { VideoCommentModel } from '../video/video-comment'
import { UserModel } from './user'
@Column
name: string
+ @AllowNull(true)
+ @Default(null)
+ @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description'))
+ @Column
+ description: string
+
@CreatedAt
createdAt: Date
const account = {
id: this.id,
displayName: this.name,
+ description: this.description,
createdAt: this.createdAt,
updatedAt: this.updatedAt
}
}
toActivityPubObject () {
- return this.Actor.toActivityPubObject(this.name, 'Account')
+ const obj = this.Actor.toActivityPubObject(this.name, 'Account')
+
+ return Object.assign(obj, {
+ summary: this.description
+ })
}
isOwned () {
import { extname } from 'path'
import * as Sequelize from 'sequelize'
import {
- AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, IsUUID, Model, Scopes,
- Table, UpdatedAt
+ AllowNull,
+ BelongsTo,
+ Column,
+ CreatedAt,
+ DataType,
+ Default,
+ DefaultScope,
+ ForeignKey,
+ HasMany,
+ HasOne,
+ Is,
+ IsUUID,
+ Model,
+ Scopes,
+ Table,
+ UpdatedAt
} from 'sequelize-typescript'
import { ActivityPubActorType } from '../../../shared/models/activitypub'
import { Avatar } from '../../../shared/models/avatars/avatar.model'
import { activityPubContextify } from '../../helpers/activitypub'
import {
- isActorFollowersCountValid, isActorFollowingCountValid, isActorPreferredUsernameValid, isActorPrivateKeyValid,
+ isActorFollowersCountValid,
+ isActorFollowingCountValid,
+ isActorPreferredUsernameValid,
+ isActorPrivateKeyValid,
isActorPublicKeyValid
} from '../../helpers/custom-validators/activitypub/actor'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import {
AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table,
- UpdatedAt
+ UpdatedAt, Default
} from 'sequelize-typescript'
import { ActivityPubActor } from '../../../shared/models/activitypub'
-import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
+import { VideoChannel } from '../../../shared/models/videos'
+import {
+ isVideoChannelDescriptionValid, isVideoChannelNameValid,
+ isVideoChannelSupportValid
+} from '../../helpers/custom-validators/video-channels'
import { logger } from '../../helpers/logger'
import { sendDeleteActor } from '../../lib/activitypub/send'
import { AccountModel } from '../account/account'
name: string
@AllowNull(true)
+ @Default(null)
@Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description'))
@Column
description: string
+ @AllowNull(true)
+ @Default(null)
+ @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support'))
+ @Column
+ support: string
+
@CreatedAt
createdAt: Date
.findById(id, options)
}
- toFormattedJSON () {
+ toFormattedJSON (): VideoChannel {
const actor = this.Actor.toFormattedJSON()
const account = {
id: this.id,
displayName: this.name,
description: this.description,
+ support: this.support,
isLocal: this.Actor.isOwned(),
createdAt: this.createdAt,
updatedAt: this.updatedAt
return Object.assign(obj, {
summary: this.description,
+ support: this.support,
attributedTo: [
{
type: 'Person' as 'Person',
import * as Sequelize from 'sequelize'
+import * as Bluebird from 'bluebird'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { CONSTRAINTS_FIELDS } from '../../initializers'
.then(res => res.map(r => r.Actor))
}
- static loadActorsByVideoOwner (actorOwnerId: number, t: Sequelize.Transaction) {
+ static loadActorsByVideoOwner (actorOwnerId: number, t: Sequelize.Transaction): Bluebird<ActorModel[]> {
const query = {
attributes: [],
include: [
return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
.then(res => res.map(r => r.Actor))
}
+
+ static loadActorsByVideoChannel (videoChannelId: number, t: Sequelize.Transaction): Bluebird<ActorModel[]> {
+ const query = {
+ attributes: [],
+ include: [
+ {
+ model: ActorModel,
+ required: true
+ },
+ {
+ attributes: [],
+ model: VideoModel,
+ required: true,
+ where: {
+ channelId: videoChannelId
+ }
+ }
+ ],
+ transaction: t
+ }
+
+ return VideoShareModel.scope(ScopeNames.FULL)
+ .findAll(query)
+ .then(res => res.map(r => r.Actor))
+ }
}
isVideoLanguageValid,
isVideoLicenceValid,
isVideoNameValid,
- isVideoPrivacyValid
+ isVideoPrivacyValid, isVideoSupportValid
} from '../../helpers/custom-validators/videos'
import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
import { logger } from '../../helpers/logger'
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max))
description: string
+ @AllowNull(true)
+ @Default(null)
+ @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support'))
+ @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max))
+ support: string
+
@AllowNull(false)
@Is('VideoDuration', value => throwIfNotValid(value, isVideoDurationValid, 'duration'))
@Column
return join(STATIC_PATHS.PREVIEWS, this.getPreviewName())
}
- toFormattedJSON () {
+ toFormattedJSON (): Video {
let serverHost
if (this.VideoChannel.Account.Actor.Server) {
embedPath: this.getEmbedPath(),
createdAt: this.createdAt,
updatedAt: this.updatedAt
- } as Video
+ }
}
- toFormattedDetailsJSON () {
+ toFormattedDetailsJSON (): VideoDetails {
const formattedJson = this.toFormattedJSON()
// Maybe our server is not up to date and there are new privacy settings since our version
const detailsJson = {
privacyLabel,
privacy: this.privacy,
+ support: this.support,
descriptionPath: this.getDescriptionPath(),
channel: this.VideoChannel.toFormattedJSON(),
account: this.VideoChannel.Account.toFormattedJSON(),
return -1
})
- return Object.assign(formattedJson, detailsJson) as VideoDetails
+ return Object.assign(formattedJson, detailsJson)
}
toActivityPubObject (): VideoTorrentObject {
let dislikesObject
if (Array.isArray(this.AccountVideoRates)) {
- const likes: string[] = []
- const dislikes: string[] = []
-
- for (const rate of this.AccountVideoRates) {
- if (rate.type === 'like') {
- likes.push(rate.Account.Actor.url)
- } else if (rate.type === 'dislike') {
- dislikes.push(rate.Account.Actor.url)
- }
- }
-
const res = this.toRatesActivityPubObjects()
likesObject = res.likesObject
dislikesObject = res.dislikesObject
updated: this.updatedAt.toISOString(),
mediaType: 'text/markdown',
content: this.getTruncatedDescription(),
+ support: this.support,
icon: {
type: 'Image',
url: this.getThumbnailUrl(baseUrlHttp),
await makePutBodyRequest({ url: server.url, path: path + 'me', token: 'super token', fields, statusCodeExpected: 401 })
})
+ it('Should fail with a too long description', async function () {
+ const fields = {
+ description: 'super'.repeat(60)
+ }
+
+ await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
+ })
+
it('Should succeed with the correct params', async function () {
const fields = {
password: 'my super password',
describe('When adding a video channel', function () {
const baseCorrectParams = {
name: 'hello',
- description: 'super description'
+ description: 'super description',
+ support: 'super support text'
}
it('Should fail with a non authenticated user', async function () {
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
})
+ it('Should fail with a long support text', async function () {
+ const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
+ await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+ })
+
it('Should succeed with the correct parameters', async function () {
await makePostBodyRequest({
url: server.url,
path,
token: server.accessToken,
fields: baseCorrectParams,
- statusCodeExpected: 204
+ statusCodeExpected: 200
})
})
})
await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields })
})
+ it('Should fail with a long support text', async function () {
+ const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
+ await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields })
+ })
+
it('Should succeed with the correct parameters', async function () {
await makePutBodyRequest({
url: server.url,
nsfw: false,
commentsEnabled: true,
description: 'my super description',
+ support: 'my super support text',
tags: [ 'tag1', 'tag2' ],
privacy: VideoPrivacy.PUBLIC,
channelId
})
it('Should fail with a long description', async function () {
- const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
+ const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) })
+ const attaches = baseCorrectAttaches
+
+ await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
+ })
+
+ it('Should fail with a long support text', async function () {
+ const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
const attaches = baseCorrectAttaches
await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
})
it('Should fail with a long description', async function () {
- const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) })
+ const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) })
+
+ await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
+ })
+
+ it('Should fail with a long support text', async function () {
+ const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
})
language: 3,
nsfw: true,
description: 'my super description',
+ support: 'my super support text',
host: 'localhost:9003',
account: 'root',
isLocal,
nsfw: true,
privacy: VideoPrivacy.PUBLIC,
description: 'my super description for server 1',
+ support: 'my super support text for server 1',
tags: [ 'tag1p1', 'tag2p1' ],
fixture: 'video_short1.webm'
}
language: 9,
nsfw: true,
description: 'my super description for server 1',
+ support: 'my super support text for server 1',
host: 'localhost:9001',
account: 'root',
isLocal: false,
import * as chai from 'chai'
import 'mocha'
import { Account } from '../../../../shared/models/actors'
-import { checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, userLogin, wait } from '../../utils'
+import {
+ checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, updateMyUser, userLogin,
+ wait
+} from '../../utils'
import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index'
import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts'
import { setAccessTokensToServers } from '../../utils/users/login'
await wait(5000)
})
+ it('Should be able to update my description', async function () {
+ this.timeout(10000)
+
+ await updateMyUser({
+ url: servers[0].url,
+ accessToken: servers[0].accessToken,
+ description: 'my super description updated'
+ })
+
+ const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
+ user = res.body
+ expect(user.account.description).to.equal('my super description updated')
+
+ await wait(5000)
+ })
+
it('Should be able to update my avatar', async function () {
this.timeout(10000)
await wait(5000)
})
- it('Should have updated my avatar on other servers too', async function () {
+ it('Should have updated my avatar and my description on other servers too', async function () {
for (const server of servers) {
const resAccounts = await getAccountsList(server.url, '-createdAt')
const rootServer1Get = resAccount.body as Account
expect(rootServer1Get.name).to.equal('root')
expect(rootServer1Get.host).to.equal('localhost:9001')
+ expect(rootServer1Get.description).to.equal('my super description updated')
await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png')
}
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
expect(user.roleLabel).to.equal('User')
expect(user.id).to.be.a('number')
+ expect(user.account.description).to.be.null
})
it('Should be able to upload a video with this user', async function () {
expect(user.displayNSFW).to.be.ok
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
expect(user.id).to.be.a('number')
+ expect(user.account.description).to.be.null
})
it('Should be able to change the autoPlayVideo attribute', async function () {
expect(user.displayNSFW).to.be.ok
expect(user.videoQuota).to.equal(2 * 1024 * 1024)
expect(user.id).to.be.a('number')
+ expect(user.account.description).to.be.null
})
it('Should be able to update my avatar', async function () {
await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.png')
})
+ it('Should be able to update my description', async function () {
+ await updateMyUser({
+ url: server.url,
+ accessToken: accessTokenUser,
+ description: 'my super description updated'
+ })
+
+ const res = await getMyUserInformation(server.url, accessTokenUser)
+ const user = res.body
+
+ expect(user.username).to.equal('user_1')
+ expect(user.email).to.equal('updated@example.com')
+ expect(user.displayNSFW).to.be.ok
+ expect(user.videoQuota).to.equal(2 * 1024 * 1024)
+ expect(user.id).to.be.a('number')
+ expect(user.account.description).to.equal('my super description updated')
+ })
+
it('Should be able to update another user', async function () {
await updateUser({
url: server.url,
language: 9,
nsfw: true,
description: 'my super description for server 1',
+ support: 'my super support text for server 1',
tags: [ 'tag1p1', 'tag2p1' ],
channelId: videoChannelId,
fixture: 'video_short1.webm'
language: 9,
nsfw: true,
description: 'my super description for server 1',
+ support: 'my super support text for server 1',
host: 'localhost:9001',
account: 'root',
isLocal,
language: 11,
nsfw: true,
description: 'my super description for server 2',
+ support: 'my super support text for server 2',
tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
fixture: 'video_short2.webm',
thumbnailfile: 'thumbnail.jpg',
language: 11,
nsfw: true,
description: 'my super description for server 2',
+ support: 'my super support text for server 2',
host: 'localhost:9002',
account: 'user1',
isLocal,
language: 11,
nsfw: true,
description: 'my super description for server 3',
+ support: 'my super support text for server 3',
tags: [ 'tag1p3' ],
fixture: 'video_short3.webm'
}
language: 12,
nsfw: false,
description: 'my super description for server 3-2',
+ support: 'my super support text for server 3-2',
tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
fixture: 'video_short.webm'
}
language: 11,
nsfw: true,
description: 'my super description for server 3',
+ support: 'my super support text for server 3',
host: 'localhost:9003',
account: 'root',
isLocal,
language: 12,
nsfw: false,
description: 'my super description for server 3-2',
+ support: 'my super support text for server 3-2',
host: 'localhost:9003',
account: 'root',
commentsEnabled: true,
language: 13,
nsfw: true,
description: 'my super description updated',
+ support: 'my super support text updated',
tags: [ 'tag_up_1', 'tag_up_2' ],
thumbnailfile: 'thumbnail.jpg',
previewfile: 'preview.jpg'
language: 13,
nsfw: true,
description: 'my super description updated',
+ support: 'my super support text updated',
host: 'localhost:9003',
account: 'root',
isLocal,
language: null,
nsfw: false,
description: null,
+ support: null,
host: 'localhost:9002',
account: 'root',
isLocal,
language: 3,
nsfw: true,
description: 'my super description',
+ support: 'my super support text',
host: 'localhost:9001',
account: 'root',
isLocal: true,
language: 5,
nsfw: false,
description: 'my super description updated',
+ support: 'my super support text updated',
host: 'localhost:9001',
account: 'root',
isLocal: true,
/* tslint:disable:no-unused-expression */
-import 'mocha'
import * as chai from 'chai'
-const expect = chai.expect
-
+import 'mocha'
+import { User } from '../../../../shared/index'
+import { doubleFollow, flushAndRunMultipleServers, uploadVideo, wait } from '../../utils'
import {
- ServerInfo,
+ addVideoChannel,
+ deleteVideoChannel,
flushTests,
- runServer,
- setAccessTokensToServers,
- killallServers,
+ getAccountVideoChannelsList,
getMyUserInformation,
+ getVideoChannel,
getVideoChannelsList,
- addVideoChannel,
- getAccountVideoChannelsList,
- updateVideoChannel,
- deleteVideoChannel,
- getVideoChannel
+ killallServers,
+ ServerInfo,
+ setAccessTokensToServers,
+ updateVideoChannel
} from '../../utils/index'
-import { User } from '../../../../shared/index'
-describe('Test a video channels', function () {
- let server: ServerInfo
+const expect = chai.expect
+
+describe('Test video channels', function () {
+ let servers: ServerInfo[]
let userInfo: User
let videoChannelId: number
await flushTests()
- server = await runServer(1)
+ servers = await flushAndRunMultipleServers(2)
+
+ await setAccessTokensToServers(servers)
+ await doubleFollow(servers[0], servers[1])
- await setAccessTokensToServers([ server ])
+ await wait(5000)
})
it('Should have one video channel (created with root)', async () => {
- const res = await getVideoChannelsList(server.url, 0, 2)
+ const res = await getVideoChannelsList(servers[0].url, 0, 2)
expect(res.body.total).to.equal(1)
expect(res.body.data).to.be.an('array')
expect(res.body.data).to.have.lengthOf(1)
})
- it('Should create another video channel', async () => {
+ it('Should create another video channel', async function () {
+ this.timeout(10000)
+
const videoChannel = {
name: 'second video channel',
- description: 'super video channel description'
+ description: 'super video channel description',
+ support: 'super video channel support text'
}
- await addVideoChannel(server.url, server.accessToken, videoChannel)
+ const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
+ videoChannelId = res.body.videoChannel.id
+
+ // The channel is 1 is propagated to servers 2
+ await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: videoChannelId })
+
+ await wait(3000)
})
it('Should have two video channels when getting my information', async () => {
- const res = await getMyUserInformation(server.url, server.accessToken)
+ const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
userInfo = res.body
expect(userInfo.videoChannels).to.be.an('array')
expect(videoChannels[0].displayName).to.equal('Default root channel')
expect(videoChannels[1].displayName).to.equal('second video channel')
expect(videoChannels[1].description).to.equal('super video channel description')
+ expect(videoChannels[1].support).to.equal('super video channel support text')
})
- it('Should have two video channels when getting account channels', async () => {
- const res = await getAccountVideoChannelsList(server.url, userInfo.account.uuid)
-
+ it('Should have two video channels when getting account channels on server 1', async function () {
+ const res = await getAccountVideoChannelsList(servers[0].url, userInfo.account.uuid)
expect(res.body.total).to.equal(2)
expect(res.body.data).to.be.an('array')
expect(res.body.data).to.have.lengthOf(2)
expect(videoChannels[0].displayName).to.equal('Default root channel')
expect(videoChannels[1].displayName).to.equal('second video channel')
expect(videoChannels[1].description).to.equal('super video channel description')
+ expect(videoChannels[1].support).to.equal('super video channel support text')
+ })
+
+ it('Should have one video channel when getting account channels on server 2', async function () {
+ const res = await getAccountVideoChannelsList(servers[1].url, userInfo.account.uuid)
+ expect(res.body.total).to.equal(1)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data).to.have.lengthOf(1)
- videoChannelId = videoChannels[1].id
+ const videoChannels = res.body.data
+ expect(videoChannels[0].displayName).to.equal('second video channel')
+ expect(videoChannels[0].description).to.equal('super video channel description')
+ expect(videoChannels[0].support).to.equal('super video channel support text')
})
- it('Should list video channels', async () => {
- const res = await getVideoChannelsList(server.url, 1, 1, '-name')
+ it('Should list video channels', async function () {
+ const res = await getVideoChannelsList(servers[0].url, 1, 1, '-name')
expect(res.body.total).to.equal(2)
expect(res.body.data).to.be.an('array')
expect(res.body.data[0].displayName).to.equal('Default root channel')
})
- it('Should update video channel', async () => {
+ it('Should update video channel', async function () {
+ this.timeout(5000)
+
const videoChannelAttributes = {
name: 'video channel updated',
- description: 'video channel description updated'
+ description: 'video channel description updated',
+ support: 'video channel support text updated'
}
- await updateVideoChannel(server.url, server.accessToken, videoChannelId, videoChannelAttributes)
+ await updateVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId, videoChannelAttributes)
+
+ await wait(3000)
})
- it('Should have video channel updated', async () => {
- const res = await getVideoChannelsList(server.url, 0, 1, '-name')
+ it('Should have video channel updated', async function () {
+ for (const server of servers) {
+ const res = await getVideoChannelsList(server.url, 0, 1, '-name')
- expect(res.body.total).to.equal(2)
- expect(res.body.data).to.be.an('array')
- expect(res.body.data).to.have.lengthOf(1)
- 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.total).to.equal(2)
+ expect(res.body.data).to.be.an('array')
+ expect(res.body.data).to.have.lengthOf(1)
+ 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].support).to.equal('video channel support text updated')
+ }
})
- it('Should get video channel', async () => {
- const res = await getVideoChannel(server.url, videoChannelId)
+ it('Should get video channel', async function () {
+ const res = await getVideoChannel(servers[0].url, videoChannelId)
const videoChannel = res.body
expect(videoChannel.displayName).to.equal('video channel updated')
expect(videoChannel.description).to.equal('video channel description updated')
+ expect(videoChannel.support).to.equal('video channel support text updated')
})
- it('Should delete video channel', async () => {
- await deleteVideoChannel(server.url, server.accessToken, videoChannelId)
+ it('Should delete video channel', async function () {
+ await deleteVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId)
})
- it('Should have video channel deleted', async () => {
- const res = await getVideoChannelsList(server.url, 0, 10)
+ it('Should have video channel deleted', async function () {
+ const res = await getVideoChannelsList(servers[0].url, 0, 10)
expect(res.body.total).to.equal(1)
expect(res.body.data).to.be.an('array')
})
after(async function () {
- killallServers([ server ])
+ killallServers(servers)
// Keep the logs if the test failed
if (this['ok']) {
displayNSFW?: boolean,
email?: string,
autoPlayVideo?: boolean
+ description?: string
}) {
const path = '/api/v1/users/me'
if (options.displayNSFW !== undefined && options.displayNSFW !== null) toSend['displayNSFW'] = options.displayNSFW
if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
+ if (options.description !== undefined && options.description !== null) toSend['description'] = options.description
return makePutBodyRequest({
url: options.url,
type VideoChannelAttributes = {
name?: string
description?: string
+ support?: string
}
function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
.expect('Content-Type', /json/)
}
-function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 204) {
+function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 200) {
const path = '/api/v1/videos/channels'
// Default attributes
let attributes = {
name: 'my super video channel',
- description: 'my super channel description'
+ description: 'my super channel description',
+ support: 'my super channel support'
}
attributes = Object.assign(attributes, videoChannelAttributesArg)
if (attributes.name) body['name'] = attributes.name
if (attributes.description) body['description'] = attributes.description
+ if (attributes.support) body['support'] = attributes.support
return request(url)
.put(path)
channelId: defaultChannelId,
nsfw: true,
description: 'my super description',
+ support: 'my super support text',
tags: [ 'tag' ],
privacy: VideoPrivacy.PUBLIC,
commentsEnabled: true,
req.field('licence', attributes.licence.toString())
}
+ for (let i = 0; i < attributes.tags.length; i++) {
+ req.field('tags[' + i + ']', attributes.tags[i])
+ }
+
if (attributes.thumbnailfile !== undefined) {
req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
}
req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
}
- for (let i = 0; i < attributes.tags.length; i++) {
- req.field('tags[' + i + ']', attributes.tags[i])
- }
-
return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
.expect(specialStatus)
}
nsfw: boolean
commentsEnabled: boolean
description: string
+ support: string
host: string
account: string
isLocal: boolean,
.option('-L, --language <language number>', 'Language number')
.option('-d, --video-description <description>', 'Video description')
.option('-t, --tags <tags>', 'Video tags', list)
+ .option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path')
.option('-f, --file <file>', 'Video absolute file path')
.parse(process.argv)
description: program['videoDescription'],
tags: program['tags'],
commentsEnabled: program['commentsEnabled'],
- fixture: program['file']
+ fixture: program['file'],
+ thumbnailfile: program['thumbnailPath']
}
await uploadVideo(program['url'], accessToken, videoAttributes)
summary: string
attributedTo: ActivityPubAttributedTo[]
+ support?: string
uuid: string
publicKey: {
id: string
publicKeyPem: string
}
- // Not used
icon: {
type: 'Image'
mediaType: 'image/png'
url: string
}
- // liked: string
}
updated: string
mediaType: 'text/markdown'
content: string
+ support: string
icon: ActivityIconObject
url: ActivityUrlObject[]
likes?: ActivityPubOrderedCollection<string>
export interface Account extends Actor {
displayName: string
+ description: string
}
export interface UserUpdateMe {
+ description?: string
displayNSFW?: boolean
autoPlayVideo?: boolean
email?: string
export interface VideoChannelCreate {
name: string
description?: string
+ support?: string
}
export interface VideoChannelUpdate {
name: string
- description: string
+ description?: string
+ support?: string
}
export interface VideoChannel extends Actor {
displayName: string
description: string
+ support: string
isLocal: boolean
owner?: {
name: string
licence?: number
language?: number
description?: string
+ support?: string
channelId: number
nsfw: boolean
name: string
licence?: number
language?: number
description?: string
+ support?: string
privacy?: VideoPrivacy
tags?: string[]
commentsEnabled?: boolean
privacy: VideoPrivacy
privacyLabel: string
descriptionPath: string
+ support: string
channel: VideoChannel
tags: string[]
files: VideoFile[]