1
0
Fork 0

Implement support field in video and video channel

This commit is contained in:
Chocobozzz 2018-02-15 14:46:26 +01:00
parent 34cbef8c6c
commit 2422c46b27
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
49 changed files with 490 additions and 146 deletions

View File

@ -8,6 +8,7 @@ export class Account implements ServerAccount {
url: string
name: string
displayName: string
description: string
host: string
followingCount: number
followersCount: number

View File

@ -18,6 +18,7 @@ export class VideoDetails extends Video implements VideoDetailsServerModel {
languageLabel: string
language: number
description: string
support: string
duration: number
durationLabel: string
id: number

View File

@ -9,7 +9,7 @@ import { logger } from '../../helpers/logger'
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'
@ -270,15 +270,21 @@ async function removeUser (req: express.Request, res: express.Response, next: ex
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)
}
@ -297,7 +303,7 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
const updatedActor = await updateActorAvatarInstance(actor, avatarName, t)
await updatedActor.save({ transaction: t })
await sendUpdateUser(user, t)
await sendUpdateActor(user.Account, t)
return updatedActor.Avatar
})

View File

@ -5,6 +5,7 @@ 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,
@ -80,23 +81,28 @@ async function addVideoChannelRetryWrapper (req: express.Request, res: express.R
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) {
@ -123,11 +129,10 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
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)

View File

@ -178,6 +178,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
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
@ -306,6 +307,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
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)

View File

@ -19,7 +19,8 @@ function activityPubContextify <T> (data: T) {
'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: {

View File

@ -3,12 +3,16 @@ import { Response } from 'express'
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>
@ -48,5 +52,6 @@ async function isAccountExist (p: Bluebird<AccountModel>, res: Response) {
export {
isAccountIdExist,
isLocalAccountNameExist,
isAccountDescriptionValid,
isAccountNameValid
}

View File

@ -21,6 +21,10 @@ function isUserUsernameValid (value: string) {
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))
}
@ -54,5 +58,6 @@ export {
isUserUsernameValid,
isUserDisplayNSFWValid,
isUserAutoPlayVideoValid,
isUserDescriptionValid,
isAvatarFile
}

View File

@ -16,6 +16,10 @@ function isVideoChannelNameValid (value: string) {
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)) {
@ -41,5 +45,6 @@ async function isVideoChannelExist (id: string, res: express.Response) {
export {
isVideoChannelDescriptionValid,
isVideoChannelNameValid,
isVideoChannelSupportValid,
isVideoChannelExist
}

View File

@ -42,6 +42,10 @@ function isVideoDescriptionValid (value: string) {
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)
}
@ -140,5 +144,6 @@ export {
isVideoFileResolutionValid,
isVideoFileSizeValid,
isVideoExist,
isVideoImage
isVideoImage,
isVideoSupportValid
}

View File

@ -12,7 +12,7 @@ let config: IConfig = require('config')
// ---------------------------------------------------------------------------
const LAST_MIGRATION_VERSION = 190
const LAST_MIGRATION_VERSION = 195
// ---------------------------------------------------------------------------
@ -168,6 +168,7 @@ const CONSTRAINTS_FIELDS = {
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: {
@ -176,12 +177,14 @@ const CONSTRAINTS_FIELDS = {
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: {

View File

@ -0,0 +1,53 @@
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
}

View File

@ -225,12 +225,10 @@ function saveActorAndServerAndModelIfNotExist (
})
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
}
@ -242,6 +240,7 @@ type FetchRemoteActorResult = {
actor: ActorModel
name: string
summary: string
support?: string
avatarName?: string
attributedTo: ActivityPubAttributedTo[]
}
@ -290,6 +289,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResu
name,
avatarName,
summary: actorJSON.summary,
support: actorJSON.support,
attributedTo: actorJSON.attributedTo
}
}
@ -298,6 +298,7 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t
const [ accountCreated ] = await AccountModel.findOrCreate({
defaults: {
name: result.name,
description: result.summary,
actorId: actor.id
},
where: {
@ -314,6 +315,7 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
defaults: {
name: result.name,
description: result.summary,
support: result.support,
actorId: actor.id,
accountId: ownerActor.Account.id
},
@ -352,11 +354,14 @@ async function refreshActorIfNeeded (actor: ActorModel) {
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 })
}

View File

@ -9,20 +9,24 @@ import { sequelizeTypescript } from '../../../initializers'
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
@ -75,6 +79,7 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
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)
@ -117,33 +122,36 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
}
}
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)
@ -151,18 +159,20 @@ async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate)
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

View File

@ -1,9 +1,10 @@
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'
@ -23,15 +24,23 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction) {
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)
@ -40,7 +49,7 @@ async function sendUpdateUser (user: UserModel, t: Transaction) {
// ---------------------------------------------------------------------------
export {
sendUpdateUser,
sendUpdateActor,
sendUpdateVideo
}

View File

@ -83,6 +83,11 @@ async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelMode
description = videoObject.content
}
let support = null
if (videoObject.support) {
support = videoObject.support
}
return {
name: videoObject.name,
uuid: videoObject.uuid,
@ -91,6 +96,7 @@ async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelMode
licence,
language,
description,
support,
nsfw: videoObject.sensitive,
commentsEnabled: videoObject.commentsEnabled,
channelId: videoChannel.id,

View File

@ -16,6 +16,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
const videoChannelData = {
name: videoChannelInfo.name,
description: videoChannelInfo.description,
support: videoChannelInfo.support,
accountId: account.id,
actorId: actorInstanceCreated.id
}

View File

@ -7,6 +7,7 @@ import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
import {
isAvatarFile,
isUserAutoPlayVideoValid,
isUserDescriptionValid,
isUserDisplayNSFWValid,
isUserPasswordValid,
isUserRoleValid,
@ -97,6 +98,7 @@ const usersUpdateValidator = [
]
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'),

View File

@ -5,7 +5,7 @@ import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
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'
@ -27,7 +27,8 @@ const listVideoAccountChannelsValidator = [
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 })
@ -42,6 +43,7 @@ const videoChannelsUpdateValidator = [
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 })

View File

@ -14,7 +14,7 @@ import {
isVideoLicenceValid,
isVideoNameValid,
isVideoPrivacyValid,
isVideoRatingTypeValid,
isVideoRatingTypeValid, isVideoSupportValid,
isVideoTagsValid
} from '../../helpers/custom-validators/videos'
import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
@ -46,6 +46,7 @@ const videosAddValidator = [
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'),
@ -116,6 +117,7 @@ const videosUpdateValidator = [
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'),

View File

@ -1,16 +1,28 @@
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'
@ -42,6 +54,12 @@ export class AccountModel extends Model<AccountModel> {
@Column
name: string
@AllowNull(true)
@Default(null)
@Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description'))
@Column
description: string
@CreatedAt
createdAt: Date
@ -196,6 +214,7 @@ export class AccountModel extends Model<AccountModel> {
const account = {
id: this.id,
displayName: this.name,
description: this.description,
createdAt: this.createdAt,
updatedAt: this.updatedAt
}
@ -204,7 +223,11 @@ export class AccountModel extends Model<AccountModel> {
}
toActivityPubObject () {
return this.Actor.toActivityPubObject(this.name, 'Account')
const obj = this.Actor.toActivityPubObject(this.name, 'Account')
return Object.assign(obj, {
summary: this.description
})
}
isOwned () {

View File

@ -2,14 +2,31 @@ import { values } from 'lodash'
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'

View File

@ -1,9 +1,13 @@
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'
@ -67,10 +71,17 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
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
@ -221,12 +232,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
.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
@ -240,6 +252,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
return Object.assign(obj, {
summary: this.description,
support: this.support,
attributedTo: [
{
type: 'Person' as 'Person',

View File

@ -1,4 +1,5 @@
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'
@ -115,7 +116,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
.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: [
@ -152,4 +153,29 @@ export class VideoShareModel extends Model<VideoShareModel> {
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))
}
}

View File

@ -40,7 +40,7 @@ import {
isVideoLanguageValid,
isVideoLicenceValid,
isVideoNameValid,
isVideoPrivacyValid
isVideoPrivacyValid, isVideoSupportValid
} from '../../helpers/custom-validators/videos'
import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
import { logger } from '../../helpers/logger'
@ -299,6 +299,12 @@ export class VideoModel extends Model<VideoModel> {
@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
@ -841,7 +847,7 @@ export class VideoModel extends Model<VideoModel> {
return join(STATIC_PATHS.PREVIEWS, this.getPreviewName())
}
toFormattedJSON () {
toFormattedJSON (): Video {
let serverHost
if (this.VideoChannel.Account.Actor.Server) {
@ -875,10 +881,10 @@ export class VideoModel extends Model<VideoModel> {
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
@ -888,6 +894,7 @@ export class VideoModel extends Model<VideoModel> {
const detailsJson = {
privacyLabel,
privacy: this.privacy,
support: this.support,
descriptionPath: this.getDescriptionPath(),
channel: this.VideoChannel.toFormattedJSON(),
account: this.VideoChannel.Account.toFormattedJSON(),
@ -917,7 +924,7 @@ export class VideoModel extends Model<VideoModel> {
return -1
})
return Object.assign(formattedJson, detailsJson) as VideoDetails
return Object.assign(formattedJson, detailsJson)
}
toActivityPubObject (): VideoTorrentObject {
@ -957,17 +964,6 @@ export class VideoModel extends Model<VideoModel> {
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
@ -1032,6 +1028,7 @@ export class VideoModel extends Model<VideoModel> {
updated: this.updatedAt.toISOString(),
mediaType: 'text/markdown',
content: this.getTruncatedDescription(),
support: this.support,
icon: {
type: 'Image',
url: this.getThumbnailUrl(baseUrlHttp),

View File

@ -255,6 +255,14 @@ describe('Test users API validators', function () {
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',

View File

@ -62,7 +62,8 @@ describe('Test videos API validator', function () {
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 () {
@ -89,13 +90,18 @@ describe('Test videos API validator', 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
})
})
})
@ -143,6 +149,11 @@ describe('Test videos API validator', function () {
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,

View File

@ -102,6 +102,7 @@ describe('Test videos API validator', function () {
nsfw: false,
commentsEnabled: true,
description: 'my super description',
support: 'my super support text',
tags: [ 'tag1', 'tag2' ],
privacy: VideoPrivacy.PUBLIC,
channelId
@ -178,7 +179,14 @@ describe('Test videos API validator', function () {
})
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 })
@ -417,7 +425,13 @@ describe('Test videos API validator', function () {
})
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 })
})

View File

@ -280,6 +280,7 @@ describe('Test follows', function () {
language: 3,
nsfw: true,
description: 'my super description',
support: 'my super support text',
host: 'localhost:9003',
account: 'root',
isLocal,

View File

@ -36,6 +36,7 @@ describe('Test handle downs', function () {
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'
}
@ -51,6 +52,7 @@ describe('Test handle downs', function () {
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,

View File

@ -3,7 +3,10 @@
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'
@ -51,6 +54,22 @@ describe('Test users with multiple servers', function () {
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)
@ -70,7 +89,7 @@ describe('Test users with multiple servers', function () {
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')
@ -81,6 +100,7 @@ describe('Test users with multiple servers', function () {
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')
}

View File

@ -172,6 +172,7 @@ describe('Test users', function () {
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 () {
@ -315,6 +316,7 @@ describe('Test users', 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 () {
@ -345,6 +347,7 @@ describe('Test users', 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 () {
@ -362,6 +365,24 @@ describe('Test users', 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,

View File

@ -70,6 +70,7 @@ describe('Test multiple servers', function () {
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'
@ -88,6 +89,7 @@ describe('Test multiple servers', function () {
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,
@ -136,6 +138,7 @@ describe('Test multiple servers', function () {
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',
@ -156,6 +159,7 @@ describe('Test multiple servers', function () {
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,
@ -211,6 +215,7 @@ describe('Test multiple servers', function () {
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'
}
@ -223,6 +228,7 @@ describe('Test multiple servers', function () {
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'
}
@ -257,6 +263,7 @@ describe('Test multiple servers', function () {
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,
@ -286,6 +293,7 @@ describe('Test multiple servers', function () {
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,
@ -525,6 +533,7 @@ describe('Test multiple servers', function () {
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'
@ -553,6 +562,7 @@ describe('Test multiple servers', function () {
language: 13,
nsfw: true,
description: 'my super description updated',
support: 'my super support text updated',
host: 'localhost:9003',
account: 'root',
isLocal,
@ -841,6 +851,7 @@ describe('Test multiple servers', function () {
language: null,
nsfw: false,
description: null,
support: null,
host: 'localhost:9002',
account: 'root',
isLocal,

View File

@ -26,6 +26,7 @@ describe('Test a single server', function () {
language: 3,
nsfw: true,
description: 'my super description',
support: 'my super support text',
host: 'localhost:9001',
account: 'root',
isLocal: true,
@ -54,6 +55,7 @@ describe('Test a single server', function () {
language: 5,
nsfw: false,
description: 'my super description updated',
support: 'my super support text updated',
host: 'localhost:9001',
account: 'root',
isLocal: true,

View File

@ -1,27 +1,27 @@
/* tslint:disable:no-unused-expression */
import 'mocha'
import * as chai from 'chai'
import 'mocha'
import { User } from '../../../../shared/index'
import { doubleFollow, flushAndRunMultipleServers, uploadVideo, wait } from '../../utils'
import {
addVideoChannel,
deleteVideoChannel,
flushTests,
getAccountVideoChannelsList,
getMyUserInformation,
getVideoChannel,
getVideoChannelsList,
killallServers,
ServerInfo,
setAccessTokensToServers,
updateVideoChannel
} from '../../utils/index'
const expect = chai.expect
import {
ServerInfo,
flushTests,
runServer,
setAccessTokensToServers,
killallServers,
getMyUserInformation,
getVideoChannelsList,
addVideoChannel,
getAccountVideoChannelsList,
updateVideoChannel,
deleteVideoChannel,
getVideoChannel
} from '../../utils/index'
import { User } from '../../../../shared/index'
describe('Test a video channels', function () {
let server: ServerInfo
describe('Test video channels', function () {
let servers: ServerInfo[]
let userInfo: User
let videoChannelId: number
@ -30,29 +30,41 @@ describe('Test a video channels', function () {
await flushTests()
server = await runServer(1)
servers = await flushAndRunMultipleServers(2)
await setAccessTokensToServers([ server ])
await setAccessTokensToServers(servers)
await doubleFollow(servers[0], servers[1])
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')
@ -62,11 +74,11 @@ describe('Test a video channels', function () {
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)
@ -75,12 +87,23 @@ describe('Test a video channels', function () {
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')
videoChannelId = videoChannels[1].id
expect(videoChannels[1].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 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)
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 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')
@ -88,39 +111,48 @@ describe('Test a video channels', function () {
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')
@ -129,7 +161,7 @@ describe('Test a video channels', function () {
})
after(async function () {
killallServers([ server ])
killallServers(servers)
// Keep the logs if the test failed
if (this['ok']) {

View File

@ -131,6 +131,7 @@ function updateMyUser (options: {
displayNSFW?: boolean,
email?: string,
autoPlayVideo?: boolean
description?: string
}) {
const path = '/api/v1/users/me'
@ -139,6 +140,7 @@ function updateMyUser (options: {
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,

View File

@ -3,6 +3,7 @@ import * as request from 'supertest'
type VideoChannelAttributes = {
name?: string
description?: string
support?: string
}
function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
@ -30,13 +31,14 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s
.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)
@ -54,6 +56,7 @@ function updateVideoChannel (url: string, token: string, channelId: number, attr
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)

View File

@ -248,6 +248,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
channelId: defaultChannelId,
nsfw: true,
description: 'my super description',
support: 'my super support text',
tags: [ 'tag' ],
privacy: VideoPrivacy.PUBLIC,
commentsEnabled: true,
@ -277,6 +278,10 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
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))
}
@ -284,10 +289,6 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
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)
}
@ -366,6 +367,7 @@ async function completeVideoCheck (
nsfw: boolean
commentsEnabled: boolean
description: string
support: string
host: string
account: string
isLocal: boolean,

View File

@ -19,6 +19,7 @@ program
.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)
@ -72,7 +73,8 @@ async function run () {
description: program['videoDescription'],
tags: program['tags'],
commentsEnabled: program['commentsEnabled'],
fixture: program['file']
fixture: program['file'],
thumbnailfile: program['thumbnailPath']
}
await uploadVideo(program['url'], accessToken, videoAttributes)

View File

@ -19,6 +19,7 @@ export interface ActivityPubActor {
summary: string
attributedTo: ActivityPubAttributedTo[]
support?: string
uuid: string
publicKey: {
id: string
@ -26,11 +27,9 @@ export interface ActivityPubActor {
publicKeyPem: string
}
// Not used
icon: {
type: 'Image'
mediaType: 'image/png'
url: string
}
// liked: string
}

View File

@ -23,6 +23,7 @@ export interface VideoTorrentObject {
updated: string
mediaType: 'text/markdown'
content: string
support: string
icon: ActivityIconObject
url: ActivityUrlObject[]
likes?: ActivityPubOrderedCollection<string>

View File

@ -2,4 +2,5 @@ import { Actor } from './actor.model'
export interface Account extends Actor {
displayName: string
description: string
}

View File

@ -1,4 +1,5 @@
export interface UserUpdateMe {
description?: string
displayNSFW?: boolean
autoPlayVideo?: boolean
email?: string

View File

@ -1,4 +1,5 @@
export interface VideoChannelCreate {
name: string
description?: string
support?: string
}

View File

@ -1,4 +1,5 @@
export interface VideoChannelUpdate {
name: string
description: string
description?: string
support?: string
}

View File

@ -4,6 +4,7 @@ import { Video } from './video.model'
export interface VideoChannel extends Actor {
displayName: string
description: string
support: string
isLocal: boolean
owner?: {
name: string

View File

@ -5,6 +5,7 @@ export interface VideoCreate {
licence?: number
language?: number
description?: string
support?: string
channelId: number
nsfw: boolean
name: string

View File

@ -6,6 +6,7 @@ export interface VideoUpdate {
licence?: number
language?: number
description?: string
support?: string
privacy?: VideoPrivacy
tags?: string[]
commentsEnabled?: boolean

View File

@ -41,6 +41,7 @@ export interface VideoDetails extends Video {
privacy: VideoPrivacy
privacyLabel: string
descriptionPath: string
support: string
channel: VideoChannel
tags: string[]
files: VideoFile[]