aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/users.ts16
-rw-r--r--server/controllers/api/videos/channel.ts23
-rw-r--r--server/controllers/api/videos/index.ts2
-rw-r--r--server/helpers/activitypub.ts3
-rw-r--r--server/helpers/custom-validators/accounts.ts7
-rw-r--r--server/helpers/custom-validators/users.ts5
-rw-r--r--server/helpers/custom-validators/video-channels.ts5
-rw-r--r--server/helpers/custom-validators/videos.ts7
-rw-r--r--server/initializers/constants.ts7
-rw-r--r--server/initializers/migrations/0195-support.ts53
-rw-r--r--server/lib/activitypub/actor.ts13
-rw-r--r--server/lib/activitypub/process/process-update.ts52
-rw-r--r--server/lib/activitypub/send/send-update.ts23
-rw-r--r--server/lib/activitypub/videos.ts6
-rw-r--r--server/lib/video-channel.ts1
-rw-r--r--server/middlewares/validators/users.ts2
-rw-r--r--server/middlewares/validators/video-channels.ts6
-rw-r--r--server/middlewares/validators/videos.ts4
-rw-r--r--server/models/account/account.ts29
-rw-r--r--server/models/activitypub/actor.ts23
-rw-r--r--server/models/video/video-channel.ts19
-rw-r--r--server/models/video/video-share.ts28
-rw-r--r--server/models/video/video.ts29
-rw-r--r--server/tests/api/check-params/users.ts8
-rw-r--r--server/tests/api/check-params/video-channels.ts15
-rw-r--r--server/tests/api/check-params/videos.ts18
-rw-r--r--server/tests/api/server/follows.ts1
-rw-r--r--server/tests/api/server/handle-down.ts2
-rw-r--r--server/tests/api/users/users-multiple-servers.ts24
-rw-r--r--server/tests/api/users/users.ts21
-rw-r--r--server/tests/api/videos/multiple-servers.ts11
-rw-r--r--server/tests/api/videos/single-server.ts2
-rw-r--r--server/tests/api/videos/video-channels.ts122
-rw-r--r--server/tests/utils/users/users.ts2
-rw-r--r--server/tests/utils/videos/video-channels.ts7
-rw-r--r--server/tests/utils/videos/videos.ts10
-rw-r--r--server/tools/upload.ts4
37 files changed, 472 insertions, 138 deletions
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index e3067584e..583376c38 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -9,7 +9,7 @@ import { logger } from '../../helpers/logger'
9import { createReqFiles, getFormattedObjects } from '../../helpers/utils' 9import { createReqFiles, getFormattedObjects } from '../../helpers/utils'
10import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers' 10import { AVATARS_SIZE, CONFIG, IMAGE_MIMETYPE_EXT, sequelizeTypescript } from '../../initializers'
11import { updateActorAvatarInstance } from '../../lib/activitypub' 11import { updateActorAvatarInstance } from '../../lib/activitypub'
12import { sendUpdateUser } from '../../lib/activitypub/send' 12import { sendUpdateActor } from '../../lib/activitypub/send'
13import { Emailer } from '../../lib/emailer' 13import { Emailer } from '../../lib/emailer'
14import { Redis } from '../../lib/redis' 14import { Redis } from '../../lib/redis'
15import { createUserAccountAndChannel } from '../../lib/user' 15import { createUserAccountAndChannel } from '../../lib/user'
@@ -270,15 +270,21 @@ async function removeUser (req: express.Request, res: express.Response, next: ex
270async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) { 270async function updateMe (req: express.Request, res: express.Response, next: express.NextFunction) {
271 const body: UserUpdateMe = req.body 271 const body: UserUpdateMe = req.body
272 272
273 const user = res.locals.oauth.token.user 273 const user: UserModel = res.locals.oauth.token.user
274 274
275 if (body.password !== undefined) user.password = body.password 275 if (body.password !== undefined) user.password = body.password
276 if (body.email !== undefined) user.email = body.email 276 if (body.email !== undefined) user.email = body.email
277 if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW 277 if (body.displayNSFW !== undefined) user.displayNSFW = body.displayNSFW
278 if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo 278 if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
279 279
280 await user.save() 280 await sequelizeTypescript.transaction(async t => {
281 await sendUpdateUser(user, undefined) 281 await user.save({ transaction: t })
282
283 if (body.description !== undefined) user.Account.description = body.description
284 await user.Account.save({ transaction: t })
285
286 await sendUpdateActor(user.Account, t)
287 })
282 288
283 return res.sendStatus(204) 289 return res.sendStatus(204)
284} 290}
@@ -297,7 +303,7 @@ async function updateMyAvatar (req: express.Request, res: express.Response, next
297 const updatedActor = await updateActorAvatarInstance(actor, avatarName, t) 303 const updatedActor = await updateActorAvatarInstance(actor, avatarName, t)
298 await updatedActor.save({ transaction: t }) 304 await updatedActor.save({ transaction: t })
299 305
300 await sendUpdateUser(user, t) 306 await sendUpdateActor(user.Account, t)
301 307
302 return updatedActor.Avatar 308 return updatedActor.Avatar
303 }) 309 })
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts
index 8ec53d9ae..fba5681de 100644
--- a/server/controllers/api/videos/channel.ts
+++ b/server/controllers/api/videos/channel.ts
@@ -5,6 +5,7 @@ import { logger } from '../../../helpers/logger'
5import { getFormattedObjects, resetSequelizeInstance } from '../../../helpers/utils' 5import { getFormattedObjects, resetSequelizeInstance } from '../../../helpers/utils'
6import { sequelizeTypescript } from '../../../initializers' 6import { sequelizeTypescript } from '../../../initializers'
7import { setAsyncActorKeys } from '../../../lib/activitypub' 7import { setAsyncActorKeys } from '../../../lib/activitypub'
8import { sendUpdateActor } from '../../../lib/activitypub/send'
8import { createVideoChannel } from '../../../lib/video-channel' 9import { createVideoChannel } from '../../../lib/video-channel'
9import { 10import {
10 asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setDefaultPagination, 11 asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setDefaultPagination,
@@ -80,23 +81,28 @@ async function addVideoChannelRetryWrapper (req: express.Request, res: express.R
80 errorMessage: 'Cannot insert the video video channel with many retries.' 81 errorMessage: 'Cannot insert the video video channel with many retries.'
81 } 82 }
82 83
83 await retryTransactionWrapper(addVideoChannel, options) 84 const videoChannel = await retryTransactionWrapper(addVideoChannel, options)
84 85 return res.json({
85 // TODO : include Location of the new video channel -> 201 86 videoChannel: {
86 return res.type('json').status(204).end() 87 id: videoChannel.id
88 }
89 }).end()
87} 90}
88 91
89async function addVideoChannel (req: express.Request, res: express.Response) { 92async function addVideoChannel (req: express.Request, res: express.Response) {
90 const videoChannelInfo: VideoChannelCreate = req.body 93 const videoChannelInfo: VideoChannelCreate = req.body
91 const account: AccountModel = res.locals.oauth.token.User.Account 94 const account: AccountModel = res.locals.oauth.token.User.Account
92 95
93 const videoChannelCreated = await sequelizeTypescript.transaction(async t => { 96 const videoChannelCreated: VideoChannelModel = await sequelizeTypescript.transaction(async t => {
94 return createVideoChannel(videoChannelInfo, account, t) 97 return createVideoChannel(videoChannelInfo, account, t)
95 }) 98 })
96 99
97 setAsyncActorKeys(videoChannelCreated.Actor) 100 setAsyncActorKeys(videoChannelCreated.Actor)
101 .catch(err => logger.error('Cannot set async actor keys for account %s.', videoChannelCreated.Actor.uuid, err))
98 102
99 logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid) 103 logger.info('Video channel with uuid %s created.', videoChannelCreated.Actor.uuid)
104
105 return videoChannelCreated
100} 106}
101 107
102async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { 108async function updateVideoChannelRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
@@ -123,11 +129,10 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
123 129
124 if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name) 130 if (videoChannelInfoToUpdate.name !== undefined) videoChannelInstance.set('name', videoChannelInfoToUpdate.name)
125 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description) 131 if (videoChannelInfoToUpdate.description !== undefined) videoChannelInstance.set('description', videoChannelInfoToUpdate.description)
132 if (videoChannelInfoToUpdate.support !== undefined) videoChannelInstance.set('support', videoChannelInfoToUpdate.support)
126 133
127 await videoChannelInstance.save(sequelizeOptions) 134 const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions)
128 135 await sendUpdateActor(videoChannelInstanceUpdated, t)
129 // TODO
130 // await sendUpdateVideoChannel(videoChannelInstanceUpdated, t)
131 }) 136 })
132 137
133 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid) 138 logger.info('Video channel with name %s and uuid %s updated.', videoChannelInstance.name, videoChannelInstance.Actor.uuid)
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 564ccd3f8..c9334676e 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -178,6 +178,7 @@ async function addVideo (req: express.Request, res: express.Response, videoPhysi
178 commentsEnabled: videoInfo.commentsEnabled, 178 commentsEnabled: videoInfo.commentsEnabled,
179 nsfw: videoInfo.nsfw, 179 nsfw: videoInfo.nsfw,
180 description: videoInfo.description, 180 description: videoInfo.description,
181 support: videoInfo.support,
181 privacy: videoInfo.privacy, 182 privacy: videoInfo.privacy,
182 duration: videoPhysicalFile['duration'], // duration was added by a previous middleware 183 duration: videoPhysicalFile['duration'], // duration was added by a previous middleware
183 channelId: res.locals.videoChannel.id 184 channelId: res.locals.videoChannel.id
@@ -306,6 +307,7 @@ async function updateVideo (req: express.Request, res: express.Response) {
306 if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language) 307 if (videoInfoToUpdate.language !== undefined) videoInstance.set('language', videoInfoToUpdate.language)
307 if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw) 308 if (videoInfoToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfoToUpdate.nsfw)
308 if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10)) 309 if (videoInfoToUpdate.privacy !== undefined) videoInstance.set('privacy', parseInt(videoInfoToUpdate.privacy.toString(), 10))
310 if (videoInfoToUpdate.support !== undefined) videoInstance.set('support', videoInfoToUpdate.support)
309 if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description) 311 if (videoInfoToUpdate.description !== undefined) videoInstance.set('description', videoInfoToUpdate.description)
310 if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.set('commentsEnabled', videoInfoToUpdate.commentsEnabled) 312 if (videoInfoToUpdate.commentsEnabled !== undefined) videoInstance.set('commentsEnabled', videoInfoToUpdate.commentsEnabled)
311 313
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts
index eaee324eb..d64a6dd78 100644
--- a/server/helpers/activitypub.ts
+++ b/server/helpers/activitypub.ts
@@ -19,7 +19,8 @@ function activityPubContextify <T> (data: T) {
19 'language': 'http://schema.org/inLanguage', 19 'language': 'http://schema.org/inLanguage',
20 'views': 'http://schema.org/Number', 20 'views': 'http://schema.org/Number',
21 'size': 'http://schema.org/Number', 21 'size': 'http://schema.org/Number',
22 'commentsEnabled': 'http://schema.org/Boolean' 22 'commentsEnabled': 'http://schema.org/Boolean',
23 'support': 'http://schema.org/Text'
23 }, 24 },
24 { 25 {
25 likes: { 26 likes: {
diff --git a/server/helpers/custom-validators/accounts.ts b/server/helpers/custom-validators/accounts.ts
index 8dc5d1f0d..a46ffc162 100644
--- a/server/helpers/custom-validators/accounts.ts
+++ b/server/helpers/custom-validators/accounts.ts
@@ -3,12 +3,16 @@ import { Response } from 'express'
3import 'express-validator' 3import 'express-validator'
4import * as validator from 'validator' 4import * as validator from 'validator'
5import { AccountModel } from '../../models/account/account' 5import { AccountModel } from '../../models/account/account'
6import { isUserUsernameValid } from './users' 6import { isUserDescriptionValid, isUserUsernameValid } from './users'
7 7
8function isAccountNameValid (value: string) { 8function isAccountNameValid (value: string) {
9 return isUserUsernameValid(value) 9 return isUserUsernameValid(value)
10} 10}
11 11
12function isAccountDescriptionValid (value: string) {
13 return isUserDescriptionValid(value)
14}
15
12function isAccountIdExist (id: number | string, res: Response) { 16function isAccountIdExist (id: number | string, res: Response) {
13 let promise: Bluebird<AccountModel> 17 let promise: Bluebird<AccountModel>
14 18
@@ -48,5 +52,6 @@ async function isAccountExist (p: Bluebird<AccountModel>, res: Response) {
48export { 52export {
49 isAccountIdExist, 53 isAccountIdExist,
50 isLocalAccountNameExist, 54 isLocalAccountNameExist,
55 isAccountDescriptionValid,
51 isAccountNameValid 56 isAccountNameValid
52} 57}
diff --git a/server/helpers/custom-validators/users.ts b/server/helpers/custom-validators/users.ts
index e805313f8..bbc7cc199 100644
--- a/server/helpers/custom-validators/users.ts
+++ b/server/helpers/custom-validators/users.ts
@@ -21,6 +21,10 @@ function isUserUsernameValid (value: string) {
21 return exists(value) && validator.matches(value, new RegExp(`^[a-z0-9._]{${min},${max}}$`)) 21 return exists(value) && validator.matches(value, new RegExp(`^[a-z0-9._]{${min},${max}}$`))
22} 22}
23 23
24function isUserDescriptionValid (value: string) {
25 return value === null || (exists(value) && validator.isLength(value, CONSTRAINTS_FIELDS.USERS.DESCRIPTION))
26}
27
24function isBoolean (value: any) { 28function isBoolean (value: any) {
25 return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value)) 29 return typeof value === 'boolean' || (typeof value === 'string' && validator.isBoolean(value))
26} 30}
@@ -54,5 +58,6 @@ export {
54 isUserUsernameValid, 58 isUserUsernameValid,
55 isUserDisplayNSFWValid, 59 isUserDisplayNSFWValid,
56 isUserAutoPlayVideoValid, 60 isUserAutoPlayVideoValid,
61 isUserDescriptionValid,
57 isAvatarFile 62 isAvatarFile
58} 63}
diff --git a/server/helpers/custom-validators/video-channels.ts b/server/helpers/custom-validators/video-channels.ts
index 6bc96bf51..2a6f56840 100644
--- a/server/helpers/custom-validators/video-channels.ts
+++ b/server/helpers/custom-validators/video-channels.ts
@@ -16,6 +16,10 @@ function isVideoChannelNameValid (value: string) {
16 return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME) 16 return exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.NAME)
17} 17}
18 18
19function isVideoChannelSupportValid (value: string) {
20 return value === null || (exists(value) && validator.isLength(value, VIDEO_CHANNELS_CONSTRAINTS_FIELDS.SUPPORT))
21}
22
19async function isVideoChannelExist (id: string, res: express.Response) { 23async function isVideoChannelExist (id: string, res: express.Response) {
20 let videoChannel: VideoChannelModel 24 let videoChannel: VideoChannelModel
21 if (validator.isInt(id)) { 25 if (validator.isInt(id)) {
@@ -41,5 +45,6 @@ async function isVideoChannelExist (id: string, res: express.Response) {
41export { 45export {
42 isVideoChannelDescriptionValid, 46 isVideoChannelDescriptionValid,
43 isVideoChannelNameValid, 47 isVideoChannelNameValid,
48 isVideoChannelSupportValid,
44 isVideoChannelExist 49 isVideoChannelExist
45} 50}
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 8ef3a3c64..a46d715ba 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -42,6 +42,10 @@ function isVideoDescriptionValid (value: string) {
42 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION)) 42 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.DESCRIPTION))
43} 43}
44 44
45function isVideoSupportValid (value: string) {
46 return value === null || (exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.SUPPORT))
47}
48
45function isVideoNameValid (value: string) { 49function isVideoNameValid (value: string) {
46 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME) 50 return exists(value) && validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.NAME)
47} 51}
@@ -140,5 +144,6 @@ export {
140 isVideoFileResolutionValid, 144 isVideoFileResolutionValid,
141 isVideoFileSizeValid, 145 isVideoFileSizeValid,
142 isVideoExist, 146 isVideoExist,
143 isVideoImage 147 isVideoImage,
148 isVideoSupportValid
144} 149}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 91fbbde75..ac001bbc7 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -12,7 +12,7 @@ let config: IConfig = require('config')
12 12
13// --------------------------------------------------------------------------- 13// ---------------------------------------------------------------------------
14 14
15const LAST_MIGRATION_VERSION = 190 15const LAST_MIGRATION_VERSION = 195
16 16
17// --------------------------------------------------------------------------- 17// ---------------------------------------------------------------------------
18 18
@@ -168,6 +168,7 @@ const CONSTRAINTS_FIELDS = {
168 USERS: { 168 USERS: {
169 USERNAME: { min: 3, max: 20 }, // Length 169 USERNAME: { min: 3, max: 20 }, // Length
170 PASSWORD: { min: 6, max: 255 }, // Length 170 PASSWORD: { min: 6, max: 255 }, // Length
171 DESCRIPTION: { min: 3, max: 250 }, // Length
171 VIDEO_QUOTA: { min: -1 } 172 VIDEO_QUOTA: { min: -1 }
172 }, 173 },
173 VIDEO_ABUSES: { 174 VIDEO_ABUSES: {
@@ -176,12 +177,14 @@ const CONSTRAINTS_FIELDS = {
176 VIDEO_CHANNELS: { 177 VIDEO_CHANNELS: {
177 NAME: { min: 3, max: 120 }, // Length 178 NAME: { min: 3, max: 120 }, // Length
178 DESCRIPTION: { min: 3, max: 250 }, // Length 179 DESCRIPTION: { min: 3, max: 250 }, // Length
180 SUPPORT: { min: 3, max: 300 }, // Length
179 URL: { min: 3, max: 2000 } // Length 181 URL: { min: 3, max: 2000 } // Length
180 }, 182 },
181 VIDEOS: { 183 VIDEOS: {
182 NAME: { min: 3, max: 120 }, // Length 184 NAME: { min: 3, max: 120 }, // Length
183 TRUNCATED_DESCRIPTION: { min: 3, max: 250 }, // Length 185 TRUNCATED_DESCRIPTION: { min: 3, max: 250 }, // Length
184 DESCRIPTION: { min: 3, max: 3000 }, // Length 186 DESCRIPTION: { min: 3, max: 10000 }, // Length
187 SUPPORT: { min: 3, max: 300 }, // Length
185 IMAGE: { 188 IMAGE: {
186 EXTNAME: [ '.jpg', '.jpeg' ], 189 EXTNAME: [ '.jpg', '.jpeg' ],
187 FILE_SIZE: { 190 FILE_SIZE: {
diff --git a/server/initializers/migrations/0195-support.ts b/server/initializers/migrations/0195-support.ts
new file mode 100644
index 000000000..8722a5f22
--- /dev/null
+++ b/server/initializers/migrations/0195-support.ts
@@ -0,0 +1,53 @@
1import * as Sequelize from 'sequelize'
2import { CONSTRAINTS_FIELDS } from '../index'
3
4async function up (utils: {
5 transaction: Sequelize.Transaction,
6 queryInterface: Sequelize.QueryInterface,
7 sequelize: Sequelize.Sequelize
8}): Promise<void> {
9 {
10 const data = {
11 type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max),
12 allowNull: true,
13 defaultValue: null
14 }
15 await utils.queryInterface.addColumn('video', 'support', data)
16 }
17
18 {
19 const data = {
20 type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEO_CHANNELS.SUPPORT.max),
21 allowNull: true,
22 defaultValue: null
23 }
24 await utils.queryInterface.addColumn('videoChannel', 'support', data)
25 }
26
27 {
28 const data = {
29 type: Sequelize.STRING(CONSTRAINTS_FIELDS.USERS.DESCRIPTION.max),
30 allowNull: true,
31 defaultValue: null
32 }
33 await utils.queryInterface.addColumn('account', 'description', data)
34 }
35
36 {
37 const data = {
38 type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max),
39 allowNull: true,
40 defaultValue: null
41 }
42 await utils.queryInterface.changeColumn('video', 'description', data)
43 }
44}
45
46function down (options) {
47 throw new Error('Not implemented.')
48}
49
50export {
51 up,
52 down
53}
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index c3255d8ca..897acee85 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -225,12 +225,10 @@ function saveActorAndServerAndModelIfNotExist (
225 }) 225 })
226 226
227 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { 227 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') {
228 const account = await saveAccount(actorCreated, result, t) 228 actorCreated.Account = await saveAccount(actorCreated, result, t)
229 actorCreated.Account = account
230 actorCreated.Account.Actor = actorCreated 229 actorCreated.Account.Actor = actorCreated
231 } else if (actorCreated.type === 'Group') { // Video channel 230 } else if (actorCreated.type === 'Group') { // Video channel
232 const videoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) 231 actorCreated.VideoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t)
233 actorCreated.VideoChannel = videoChannel
234 actorCreated.VideoChannel.Actor = actorCreated 232 actorCreated.VideoChannel.Actor = actorCreated
235 } 233 }
236 234
@@ -242,6 +240,7 @@ type FetchRemoteActorResult = {
242 actor: ActorModel 240 actor: ActorModel
243 name: string 241 name: string
244 summary: string 242 summary: string
243 support?: string
245 avatarName?: string 244 avatarName?: string
246 attributedTo: ActivityPubAttributedTo[] 245 attributedTo: ActivityPubAttributedTo[]
247} 246}
@@ -290,6 +289,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResu
290 name, 289 name,
291 avatarName, 290 avatarName,
292 summary: actorJSON.summary, 291 summary: actorJSON.summary,
292 support: actorJSON.support,
293 attributedTo: actorJSON.attributedTo 293 attributedTo: actorJSON.attributedTo
294 } 294 }
295} 295}
@@ -298,6 +298,7 @@ async function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t
298 const [ accountCreated ] = await AccountModel.findOrCreate({ 298 const [ accountCreated ] = await AccountModel.findOrCreate({
299 defaults: { 299 defaults: {
300 name: result.name, 300 name: result.name,
301 description: result.summary,
301 actorId: actor.id 302 actorId: actor.id
302 }, 303 },
303 where: { 304 where: {
@@ -314,6 +315,7 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
314 defaults: { 315 defaults: {
315 name: result.name, 316 name: result.name,
316 description: result.summary, 317 description: result.summary,
318 support: result.support,
317 actorId: actor.id, 319 actorId: actor.id,
318 accountId: ownerActor.Account.id 320 accountId: ownerActor.Account.id
319 }, 321 },
@@ -352,11 +354,14 @@ async function refreshActorIfNeeded (actor: ActorModel) {
352 await actor.save({ transaction: t }) 354 await actor.save({ transaction: t })
353 355
354 actor.Account.set('name', result.name) 356 actor.Account.set('name', result.name)
357 actor.Account.set('description', result.summary)
355 await actor.Account.save({ transaction: t }) 358 await actor.Account.save({ transaction: t })
356 } else if (actor.VideoChannel) { 359 } else if (actor.VideoChannel) {
357 await actor.save({ transaction: t }) 360 await actor.save({ transaction: t })
358 361
359 actor.VideoChannel.set('name', result.name) 362 actor.VideoChannel.set('name', result.name)
363 actor.VideoChannel.set('description', result.summary)
364 actor.VideoChannel.set('support', result.support)
360 await actor.VideoChannel.save({ transaction: t }) 365 await actor.VideoChannel.save({ transaction: t })
361 } 366 }
362 367
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index c7ad412bc..566e5938b 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -9,20 +9,24 @@ import { sequelizeTypescript } from '../../../initializers'
9import { AccountModel } from '../../../models/account/account' 9import { AccountModel } from '../../../models/account/account'
10import { ActorModel } from '../../../models/activitypub/actor' 10import { ActorModel } from '../../../models/activitypub/actor'
11import { TagModel } from '../../../models/video/tag' 11import { TagModel } from '../../../models/video/tag'
12import { VideoChannelModel } from '../../../models/video/video-channel'
12import { VideoFileModel } from '../../../models/video/video-file' 13import { VideoFileModel } from '../../../models/video/video-file'
13import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' 14import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
14import { 15import {
15 generateThumbnailFromUrl, getOrCreateAccountAndVideoAndChannel, videoActivityObjectToDBAttributes, 16 generateThumbnailFromUrl,
17 getOrCreateAccountAndVideoAndChannel,
18 videoActivityObjectToDBAttributes,
16 videoFileActivityUrlToDBAttributes 19 videoFileActivityUrlToDBAttributes
17} from '../videos' 20} from '../videos'
18 21
19async function processUpdateActivity (activity: ActivityUpdate) { 22async function processUpdateActivity (activity: ActivityUpdate) {
20 const actor = await getOrCreateActorAndServerAndModel(activity.actor) 23 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
24 const objectType = activity.object.type
21 25
22 if (activity.object.type === 'Video') { 26 if (objectType === 'Video') {
23 return processUpdateVideo(actor, activity) 27 return processUpdateVideo(actor, activity)
24 } else if (activity.object.type === 'Person') { 28 } else if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
25 return processUpdateAccount(actor, activity) 29 return processUpdateActor(actor, activity)
26 } 30 }
27 31
28 return 32 return
@@ -75,6 +79,7 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
75 videoInstance.set('licence', videoData.licence) 79 videoInstance.set('licence', videoData.licence)
76 videoInstance.set('language', videoData.language) 80 videoInstance.set('language', videoData.language)
77 videoInstance.set('description', videoData.description) 81 videoInstance.set('description', videoData.description)
82 videoInstance.set('support', videoData.support)
78 videoInstance.set('nsfw', videoData.nsfw) 83 videoInstance.set('nsfw', videoData.nsfw)
79 videoInstance.set('commentsEnabled', videoData.commentsEnabled) 84 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
80 videoInstance.set('duration', videoData.duration) 85 videoInstance.set('duration', videoData.duration)
@@ -117,33 +122,36 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
117 } 122 }
118} 123}
119 124
120function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) { 125function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) {
121 const options = { 126 const options = {
122 arguments: [ actor, activity ], 127 arguments: [ actor, activity ],
123 errorMessage: 'Cannot update the remote account with many retries' 128 errorMessage: 'Cannot update the remote actor with many retries'
124 } 129 }
125 130
126 return retryTransactionWrapper(updateRemoteAccount, options) 131 return retryTransactionWrapper(updateRemoteActor, options)
127} 132}
128 133
129async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) { 134async function updateRemoteActor (actor: ActorModel, activity: ActivityUpdate) {
130 const accountAttributesToUpdate = activity.object as ActivityPubActor 135 const actorAttributesToUpdate = activity.object as ActivityPubActor
131 136
132 logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid) 137 logger.debug('Updating remote account "%s".', actorAttributesToUpdate.uuid)
133 let accountInstance: AccountModel 138 let accountOrChannelInstance: AccountModel | VideoChannelModel
134 let actorFieldsSave: object 139 let actorFieldsSave: object
135 let accountFieldsSave: object 140 let accountOrChannelFieldsSave: object
136 141
137 // Fetch icon? 142 // Fetch icon?
138 const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate) 143 const avatarName = await fetchAvatarIfExists(actorAttributesToUpdate)
139 144
140 try { 145 try {
141 await sequelizeTypescript.transaction(async t => { 146 await sequelizeTypescript.transaction(async t => {
142 actorFieldsSave = actor.toJSON() 147 actorFieldsSave = actor.toJSON()
143 accountInstance = actor.Account
144 accountFieldsSave = actor.Account.toJSON()
145 148
146 await updateActorInstance(actor, accountAttributesToUpdate) 149 if (actorAttributesToUpdate.type === 'Group') accountOrChannelInstance = actor.VideoChannel
150 else accountOrChannelInstance = actor.Account
151
152 accountOrChannelFieldsSave = accountOrChannelInstance.toJSON()
153
154 await updateActorInstance(actor, actorAttributesToUpdate)
147 155
148 if (avatarName !== undefined) { 156 if (avatarName !== undefined) {
149 await updateActorAvatarInstance(actor, avatarName, t) 157 await updateActorAvatarInstance(actor, avatarName, t)
@@ -151,18 +159,20 @@ async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate)
151 159
152 await actor.save({ transaction: t }) 160 await actor.save({ transaction: t })
153 161
154 actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername) 162 accountOrChannelInstance.set('name', actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername)
155 await actor.Account.save({ transaction: t }) 163 accountOrChannelInstance.set('description', actorAttributesToUpdate.summary)
164 accountOrChannelInstance.set('support', actorAttributesToUpdate.support)
165 await accountOrChannelInstance.save({ transaction: t })
156 }) 166 })
157 167
158 logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid) 168 logger.info('Remote account with uuid %s updated', actorAttributesToUpdate.uuid)
159 } catch (err) { 169 } catch (err) {
160 if (actor !== undefined && actorFieldsSave !== undefined) { 170 if (actor !== undefined && actorFieldsSave !== undefined) {
161 resetSequelizeInstance(actor, actorFieldsSave) 171 resetSequelizeInstance(actor, actorFieldsSave)
162 } 172 }
163 173
164 if (accountInstance !== undefined && accountFieldsSave !== undefined) { 174 if (accountOrChannelInstance !== undefined && accountOrChannelFieldsSave !== undefined) {
165 resetSequelizeInstance(accountInstance, accountFieldsSave) 175 resetSequelizeInstance(accountOrChannelInstance, accountOrChannelFieldsSave)
166 } 176 }
167 177
168 // This is just a debug because we will retry the insert 178 // This is just a debug because we will retry the insert
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
index e8f11edd0..622648308 100644
--- a/server/lib/activitypub/send/send-update.ts
+++ b/server/lib/activitypub/send/send-update.ts
@@ -1,9 +1,10 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub'
3import { VideoPrivacy } from '../../../../shared/models/videos' 3import { VideoPrivacy } from '../../../../shared/models/videos'
4import { UserModel } from '../../../models/account/user' 4import { AccountModel } from '../../../models/account/account'
5import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
6import { VideoModel } from '../../../models/video/video' 6import { VideoModel } from '../../../models/video/video'
7import { VideoChannelModel } from '../../../models/video/video-channel'
7import { VideoShareModel } from '../../../models/video/video-share' 8import { VideoShareModel } from '../../../models/video/video-share'
8import { getUpdateActivityPubUrl } from '../url' 9import { getUpdateActivityPubUrl } from '../url'
9import { audiencify, broadcastToFollowers, getAudience } from './misc' 10import { audiencify, broadcastToFollowers, getAudience } from './misc'
@@ -23,15 +24,23 @@ async function sendUpdateVideo (video: VideoModel, t: Transaction) {
23 return broadcastToFollowers(data, byActor, actorsInvolved, t) 24 return broadcastToFollowers(data, byActor, actorsInvolved, t)
24} 25}
25 26
26async function sendUpdateUser (user: UserModel, t: Transaction) { 27async function sendUpdateActor (accountOrChannel: AccountModel | VideoChannelModel, t: Transaction) {
27 const byActor = user.Account.Actor 28 const byActor = accountOrChannel.Actor
28 29
29 const url = getUpdateActivityPubUrl(byActor.url, byActor.updatedAt.toISOString()) 30 const url = getUpdateActivityPubUrl(byActor.url, byActor.updatedAt.toISOString())
30 const accountObject = user.Account.toActivityPubObject() 31 const accountOrChannelObject = accountOrChannel.toActivityPubObject()
31 const audience = await getAudience(byActor, t) 32 const audience = await getAudience(byActor, t)
32 const data = await updateActivityData(url, byActor, accountObject, t, audience) 33 const data = await updateActivityData(url, byActor, accountOrChannelObject, t, audience)
34
35 let actorsInvolved: ActorModel[]
36 if (accountOrChannel instanceof AccountModel) {
37 // Actors that shared my videos are involved too
38 actorsInvolved = await VideoShareModel.loadActorsByVideoOwner(byActor.id, t)
39 } else {
40 // Actors that shared videos of my channel are involved too
41 actorsInvolved = await VideoShareModel.loadActorsByVideoChannel(accountOrChannel.id, t)
42 }
33 43
34 const actorsInvolved = await VideoShareModel.loadActorsByVideoOwner(byActor.id, t)
35 actorsInvolved.push(byActor) 44 actorsInvolved.push(byActor)
36 45
37 return broadcastToFollowers(data, byActor, actorsInvolved, t) 46 return broadcastToFollowers(data, byActor, actorsInvolved, t)
@@ -40,7 +49,7 @@ async function sendUpdateUser (user: UserModel, t: Transaction) {
40// --------------------------------------------------------------------------- 49// ---------------------------------------------------------------------------
41 50
42export { 51export {
43 sendUpdateUser, 52 sendUpdateActor,
44 sendUpdateVideo 53 sendUpdateVideo
45} 54}
46 55
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 40e9318e3..e65362190 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -83,6 +83,11 @@ async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelMode
83 description = videoObject.content 83 description = videoObject.content
84 } 84 }
85 85
86 let support = null
87 if (videoObject.support) {
88 support = videoObject.support
89 }
90
86 return { 91 return {
87 name: videoObject.name, 92 name: videoObject.name,
88 uuid: videoObject.uuid, 93 uuid: videoObject.uuid,
@@ -91,6 +96,7 @@ async function videoActivityObjectToDBAttributes (videoChannel: VideoChannelMode
91 licence, 96 licence,
92 language, 97 language,
93 description, 98 description,
99 support,
94 nsfw: videoObject.sensitive, 100 nsfw: videoObject.sensitive,
95 commentsEnabled: videoObject.commentsEnabled, 101 commentsEnabled: videoObject.commentsEnabled,
96 channelId: videoChannel.id, 102 channelId: videoChannel.id,
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts
index 569b8f29d..9f7ed9297 100644
--- a/server/lib/video-channel.ts
+++ b/server/lib/video-channel.ts
@@ -16,6 +16,7 @@ async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account
16 const videoChannelData = { 16 const videoChannelData = {
17 name: videoChannelInfo.name, 17 name: videoChannelInfo.name,
18 description: videoChannelInfo.description, 18 description: videoChannelInfo.description,
19 support: videoChannelInfo.support,
19 accountId: account.id, 20 accountId: account.id,
20 actorId: actorInstanceCreated.id 21 actorId: actorInstanceCreated.id
21 } 22 }
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index cba15c8d3..49bc0bb56 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -7,6 +7,7 @@ import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
7import { 7import {
8 isAvatarFile, 8 isAvatarFile,
9 isUserAutoPlayVideoValid, 9 isUserAutoPlayVideoValid,
10 isUserDescriptionValid,
10 isUserDisplayNSFWValid, 11 isUserDisplayNSFWValid,
11 isUserPasswordValid, 12 isUserPasswordValid,
12 isUserRoleValid, 13 isUserRoleValid,
@@ -97,6 +98,7 @@ const usersUpdateValidator = [
97] 98]
98 99
99const usersUpdateMeValidator = [ 100const usersUpdateMeValidator = [
101 body('description').optional().custom(isUserDescriptionValid).withMessage('Should have a valid description'),
100 body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'), 102 body('password').optional().custom(isUserPasswordValid).withMessage('Should have a valid password'),
101 body('email').optional().isEmail().withMessage('Should have a valid email attribute'), 103 body('email').optional().isEmail().withMessage('Should have a valid email attribute'),
102 body('displayNSFW').optional().custom(isUserDisplayNSFWValid).withMessage('Should have a valid display Not Safe For Work attribute'), 104 body('displayNSFW').optional().custom(isUserDisplayNSFWValid).withMessage('Should have a valid display Not Safe For Work attribute'),
diff --git a/server/middlewares/validators/video-channels.ts b/server/middlewares/validators/video-channels.ts
index 86bddde82..fe42105e8 100644
--- a/server/middlewares/validators/video-channels.ts
+++ b/server/middlewares/validators/video-channels.ts
@@ -5,7 +5,7 @@ import { isAccountIdExist } from '../../helpers/custom-validators/accounts'
5import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc' 5import { isIdOrUUIDValid } from '../../helpers/custom-validators/misc'
6import { 6import {
7 isVideoChannelDescriptionValid, isVideoChannelExist, 7 isVideoChannelDescriptionValid, isVideoChannelExist,
8 isVideoChannelNameValid 8 isVideoChannelNameValid, isVideoChannelSupportValid
9} from '../../helpers/custom-validators/video-channels' 9} from '../../helpers/custom-validators/video-channels'
10import { logger } from '../../helpers/logger' 10import { logger } from '../../helpers/logger'
11import { UserModel } from '../../models/account/user' 11import { UserModel } from '../../models/account/user'
@@ -27,7 +27,8 @@ const listVideoAccountChannelsValidator = [
27 27
28const videoChannelsAddValidator = [ 28const videoChannelsAddValidator = [
29 body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'), 29 body('name').custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
30 body('description').custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 30 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
31 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
31 32
32 (req: express.Request, res: express.Response, next: express.NextFunction) => { 33 (req: express.Request, res: express.Response, next: express.NextFunction) => {
33 logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body }) 34 logger.debug('Checking videoChannelsAdd parameters', { parameters: req.body })
@@ -42,6 +43,7 @@ const videoChannelsUpdateValidator = [
42 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'), 43 param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
43 body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'), 44 body('name').optional().custom(isVideoChannelNameValid).withMessage('Should have a valid name'),
44 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'), 45 body('description').optional().custom(isVideoChannelDescriptionValid).withMessage('Should have a valid description'),
46 body('support').optional().custom(isVideoChannelSupportValid).withMessage('Should have a valid support text'),
45 47
46 async (req: express.Request, res: express.Response, next: express.NextFunction) => { 48 async (req: express.Request, res: express.Response, next: express.NextFunction) => {
47 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body }) 49 logger.debug('Checking videoChannelsUpdate parameters', { parameters: req.body })
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index 6d4fb907b..e91739f81 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -14,7 +14,7 @@ import {
14 isVideoLicenceValid, 14 isVideoLicenceValid,
15 isVideoNameValid, 15 isVideoNameValid,
16 isVideoPrivacyValid, 16 isVideoPrivacyValid,
17 isVideoRatingTypeValid, 17 isVideoRatingTypeValid, isVideoSupportValid,
18 isVideoTagsValid 18 isVideoTagsValid
19} from '../../helpers/custom-validators/videos' 19} from '../../helpers/custom-validators/videos'
20import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils' 20import { getDurationFromVideoFile } from '../../helpers/ffmpeg-utils'
@@ -46,6 +46,7 @@ const videosAddValidator = [
46 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'), 46 body('language').optional().custom(isVideoLanguageValid).withMessage('Should have a valid language'),
47 body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), 47 body('nsfw').custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
48 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 48 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
49 body('support').optional().custom(isVideoSupportValid).withMessage('Should have a valid support text'),
49 body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'), 50 body('channelId').custom(isIdValid).withMessage('Should have correct video channel id'),
50 body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), 51 body('privacy').custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
51 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), 52 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
@@ -116,6 +117,7 @@ const videosUpdateValidator = [
116 body('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'), 117 body('nsfw').optional().custom(isBooleanValid).withMessage('Should have a valid NSFW attribute'),
117 body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'), 118 body('privacy').optional().custom(isVideoPrivacyValid).withMessage('Should have correct video privacy'),
118 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'), 119 body('description').optional().custom(isVideoDescriptionValid).withMessage('Should have a valid description'),
120 body('support').optional().custom(isVideoSupportValid).withMessage('Should have a valid support text'),
119 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'), 121 body('tags').optional().custom(isVideoTagsValid).withMessage('Should have correct tags'),
120 body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'), 122 body('commentsEnabled').optional().custom(isBooleanValid).withMessage('Should have comments enabled boolean'),
121 123
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 20724ae0c..d8f305c37 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -1,16 +1,28 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table, 3 AllowNull,
4 BeforeDestroy,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 Default,
9 DefaultScope,
10 ForeignKey,
11 HasMany,
12 Is,
13 Model,
14 Table,
4 UpdatedAt 15 UpdatedAt
5} from 'sequelize-typescript' 16} from 'sequelize-typescript'
6import { Account } from '../../../shared/models/actors' 17import { Account } from '../../../shared/models/actors'
18import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
7import { logger } from '../../helpers/logger' 19import { logger } from '../../helpers/logger'
8import { sendDeleteActor } from '../../lib/activitypub/send' 20import { sendDeleteActor } from '../../lib/activitypub/send'
9import { ActorModel } from '../activitypub/actor' 21import { ActorModel } from '../activitypub/actor'
10import { ApplicationModel } from '../application/application' 22import { ApplicationModel } from '../application/application'
11import { AvatarModel } from '../avatar/avatar' 23import { AvatarModel } from '../avatar/avatar'
12import { ServerModel } from '../server/server' 24import { ServerModel } from '../server/server'
13import { getSort } from '../utils' 25import { getSort, throwIfNotValid } from '../utils'
14import { VideoChannelModel } from '../video/video-channel' 26import { VideoChannelModel } from '../video/video-channel'
15import { VideoCommentModel } from '../video/video-comment' 27import { VideoCommentModel } from '../video/video-comment'
16import { UserModel } from './user' 28import { UserModel } from './user'
@@ -42,6 +54,12 @@ export class AccountModel extends Model<AccountModel> {
42 @Column 54 @Column
43 name: string 55 name: string
44 56
57 @AllowNull(true)
58 @Default(null)
59 @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description'))
60 @Column
61 description: string
62
45 @CreatedAt 63 @CreatedAt
46 createdAt: Date 64 createdAt: Date
47 65
@@ -196,6 +214,7 @@ export class AccountModel extends Model<AccountModel> {
196 const account = { 214 const account = {
197 id: this.id, 215 id: this.id,
198 displayName: this.name, 216 displayName: this.name,
217 description: this.description,
199 createdAt: this.createdAt, 218 createdAt: this.createdAt,
200 updatedAt: this.updatedAt 219 updatedAt: this.updatedAt
201 } 220 }
@@ -204,7 +223,11 @@ export class AccountModel extends Model<AccountModel> {
204 } 223 }
205 224
206 toActivityPubObject () { 225 toActivityPubObject () {
207 return this.Actor.toActivityPubObject(this.name, 'Account') 226 const obj = this.Actor.toActivityPubObject(this.name, 'Account')
227
228 return Object.assign(obj, {
229 summary: this.description
230 })
208 } 231 }
209 232
210 isOwned () { 233 isOwned () {
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index c79bba96b..1d0e54ee3 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -2,14 +2,31 @@ import { values } from 'lodash'
2import { extname } from 'path' 2import { extname } from 'path'
3import * as Sequelize from 'sequelize' 3import * as Sequelize from 'sequelize'
4import { 4import {
5 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, DefaultScope, ForeignKey, HasMany, HasOne, Is, IsUUID, Model, Scopes, 5 AllowNull,
6 Table, UpdatedAt 6 BelongsTo,
7 Column,
8 CreatedAt,
9 DataType,
10 Default,
11 DefaultScope,
12 ForeignKey,
13 HasMany,
14 HasOne,
15 Is,
16 IsUUID,
17 Model,
18 Scopes,
19 Table,
20 UpdatedAt
7} from 'sequelize-typescript' 21} from 'sequelize-typescript'
8import { ActivityPubActorType } from '../../../shared/models/activitypub' 22import { ActivityPubActorType } from '../../../shared/models/activitypub'
9import { Avatar } from '../../../shared/models/avatars/avatar.model' 23import { Avatar } from '../../../shared/models/avatars/avatar.model'
10import { activityPubContextify } from '../../helpers/activitypub' 24import { activityPubContextify } from '../../helpers/activitypub'
11import { 25import {
12 isActorFollowersCountValid, isActorFollowingCountValid, isActorPreferredUsernameValid, isActorPrivateKeyValid, 26 isActorFollowersCountValid,
27 isActorFollowingCountValid,
28 isActorPreferredUsernameValid,
29 isActorPrivateKeyValid,
13 isActorPublicKeyValid 30 isActorPublicKeyValid
14} from '../../helpers/custom-validators/activitypub/actor' 31} from '../../helpers/custom-validators/activitypub/actor'
15import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 32import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 7c161c864..289775a0f 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,9 +1,13 @@
1import { 1import {
2 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table, 2 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table,
3 UpdatedAt 3 UpdatedAt, Default
4} from 'sequelize-typescript' 4} from 'sequelize-typescript'
5import { ActivityPubActor } from '../../../shared/models/activitypub' 5import { ActivityPubActor } from '../../../shared/models/activitypub'
6import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' 6import { VideoChannel } from '../../../shared/models/videos'
7import {
8 isVideoChannelDescriptionValid, isVideoChannelNameValid,
9 isVideoChannelSupportValid
10} from '../../helpers/custom-validators/video-channels'
7import { logger } from '../../helpers/logger' 11import { logger } from '../../helpers/logger'
8import { sendDeleteActor } from '../../lib/activitypub/send' 12import { sendDeleteActor } from '../../lib/activitypub/send'
9import { AccountModel } from '../account/account' 13import { AccountModel } from '../account/account'
@@ -67,10 +71,17 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
67 name: string 71 name: string
68 72
69 @AllowNull(true) 73 @AllowNull(true)
74 @Default(null)
70 @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description')) 75 @Is('VideoChannelDescription', value => throwIfNotValid(value, isVideoChannelDescriptionValid, 'description'))
71 @Column 76 @Column
72 description: string 77 description: string
73 78
79 @AllowNull(true)
80 @Default(null)
81 @Is('VideoChannelSupport', value => throwIfNotValid(value, isVideoChannelSupportValid, 'support'))
82 @Column
83 support: string
84
74 @CreatedAt 85 @CreatedAt
75 createdAt: Date 86 createdAt: Date
76 87
@@ -221,12 +232,13 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
221 .findById(id, options) 232 .findById(id, options)
222 } 233 }
223 234
224 toFormattedJSON () { 235 toFormattedJSON (): VideoChannel {
225 const actor = this.Actor.toFormattedJSON() 236 const actor = this.Actor.toFormattedJSON()
226 const account = { 237 const account = {
227 id: this.id, 238 id: this.id,
228 displayName: this.name, 239 displayName: this.name,
229 description: this.description, 240 description: this.description,
241 support: this.support,
230 isLocal: this.Actor.isOwned(), 242 isLocal: this.Actor.isOwned(),
231 createdAt: this.createdAt, 243 createdAt: this.createdAt,
232 updatedAt: this.updatedAt 244 updatedAt: this.updatedAt
@@ -240,6 +252,7 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
240 252
241 return Object.assign(obj, { 253 return Object.assign(obj, {
242 summary: this.description, 254 summary: this.description,
255 support: this.support,
243 attributedTo: [ 256 attributedTo: [
244 { 257 {
245 type: 'Person' as 'Person', 258 type: 'Person' as 'Person',
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts
index 48ba68a4a..6f770957f 100644
--- a/server/models/video/video-share.ts
+++ b/server/models/video/video-share.ts
@@ -1,4 +1,5 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import * as Bluebird from 'bluebird'
2import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 3import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
3import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 4import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
4import { CONSTRAINTS_FIELDS } from '../../initializers' 5import { CONSTRAINTS_FIELDS } from '../../initializers'
@@ -115,7 +116,7 @@ export class VideoShareModel extends Model<VideoShareModel> {
115 .then(res => res.map(r => r.Actor)) 116 .then(res => res.map(r => r.Actor))
116 } 117 }
117 118
118 static loadActorsByVideoOwner (actorOwnerId: number, t: Sequelize.Transaction) { 119 static loadActorsByVideoOwner (actorOwnerId: number, t: Sequelize.Transaction): Bluebird<ActorModel[]> {
119 const query = { 120 const query = {
120 attributes: [], 121 attributes: [],
121 include: [ 122 include: [
@@ -152,4 +153,29 @@ export class VideoShareModel extends Model<VideoShareModel> {
152 return VideoShareModel.scope(ScopeNames.FULL).findAll(query) 153 return VideoShareModel.scope(ScopeNames.FULL).findAll(query)
153 .then(res => res.map(r => r.Actor)) 154 .then(res => res.map(r => r.Actor))
154 } 155 }
156
157 static loadActorsByVideoChannel (videoChannelId: number, t: Sequelize.Transaction): Bluebird<ActorModel[]> {
158 const query = {
159 attributes: [],
160 include: [
161 {
162 model: ActorModel,
163 required: true
164 },
165 {
166 attributes: [],
167 model: VideoModel,
168 required: true,
169 where: {
170 channelId: videoChannelId
171 }
172 }
173 ],
174 transaction: t
175 }
176
177 return VideoShareModel.scope(ScopeNames.FULL)
178 .findAll(query)
179 .then(res => res.map(r => r.Actor))
180 }
155} 181}
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index ff82fb3b2..d748e81bd 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -40,7 +40,7 @@ import {
40 isVideoLanguageValid, 40 isVideoLanguageValid,
41 isVideoLicenceValid, 41 isVideoLicenceValid,
42 isVideoNameValid, 42 isVideoNameValid,
43 isVideoPrivacyValid 43 isVideoPrivacyValid, isVideoSupportValid
44} from '../../helpers/custom-validators/videos' 44} from '../../helpers/custom-validators/videos'
45import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' 45import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
46import { logger } from '../../helpers/logger' 46import { logger } from '../../helpers/logger'
@@ -299,6 +299,12 @@ export class VideoModel extends Model<VideoModel> {
299 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max)) 299 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max))
300 description: string 300 description: string
301 301
302 @AllowNull(true)
303 @Default(null)
304 @Is('VideoSupport', value => throwIfNotValid(value, isVideoSupportValid, 'support'))
305 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.SUPPORT.max))
306 support: string
307
302 @AllowNull(false) 308 @AllowNull(false)
303 @Is('VideoDuration', value => throwIfNotValid(value, isVideoDurationValid, 'duration')) 309 @Is('VideoDuration', value => throwIfNotValid(value, isVideoDurationValid, 'duration'))
304 @Column 310 @Column
@@ -841,7 +847,7 @@ export class VideoModel extends Model<VideoModel> {
841 return join(STATIC_PATHS.PREVIEWS, this.getPreviewName()) 847 return join(STATIC_PATHS.PREVIEWS, this.getPreviewName())
842 } 848 }
843 849
844 toFormattedJSON () { 850 toFormattedJSON (): Video {
845 let serverHost 851 let serverHost
846 852
847 if (this.VideoChannel.Account.Actor.Server) { 853 if (this.VideoChannel.Account.Actor.Server) {
@@ -875,10 +881,10 @@ export class VideoModel extends Model<VideoModel> {
875 embedPath: this.getEmbedPath(), 881 embedPath: this.getEmbedPath(),
876 createdAt: this.createdAt, 882 createdAt: this.createdAt,
877 updatedAt: this.updatedAt 883 updatedAt: this.updatedAt
878 } as Video 884 }
879 } 885 }
880 886
881 toFormattedDetailsJSON () { 887 toFormattedDetailsJSON (): VideoDetails {
882 const formattedJson = this.toFormattedJSON() 888 const formattedJson = this.toFormattedJSON()
883 889
884 // Maybe our server is not up to date and there are new privacy settings since our version 890 // 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> {
888 const detailsJson = { 894 const detailsJson = {
889 privacyLabel, 895 privacyLabel,
890 privacy: this.privacy, 896 privacy: this.privacy,
897 support: this.support,
891 descriptionPath: this.getDescriptionPath(), 898 descriptionPath: this.getDescriptionPath(),
892 channel: this.VideoChannel.toFormattedJSON(), 899 channel: this.VideoChannel.toFormattedJSON(),
893 account: this.VideoChannel.Account.toFormattedJSON(), 900 account: this.VideoChannel.Account.toFormattedJSON(),
@@ -917,7 +924,7 @@ export class VideoModel extends Model<VideoModel> {
917 return -1 924 return -1
918 }) 925 })
919 926
920 return Object.assign(formattedJson, detailsJson) as VideoDetails 927 return Object.assign(formattedJson, detailsJson)
921 } 928 }
922 929
923 toActivityPubObject (): VideoTorrentObject { 930 toActivityPubObject (): VideoTorrentObject {
@@ -957,17 +964,6 @@ export class VideoModel extends Model<VideoModel> {
957 let dislikesObject 964 let dislikesObject
958 965
959 if (Array.isArray(this.AccountVideoRates)) { 966 if (Array.isArray(this.AccountVideoRates)) {
960 const likes: string[] = []
961 const dislikes: string[] = []
962
963 for (const rate of this.AccountVideoRates) {
964 if (rate.type === 'like') {
965 likes.push(rate.Account.Actor.url)
966 } else if (rate.type === 'dislike') {
967 dislikes.push(rate.Account.Actor.url)
968 }
969 }
970
971 const res = this.toRatesActivityPubObjects() 967 const res = this.toRatesActivityPubObjects()
972 likesObject = res.likesObject 968 likesObject = res.likesObject
973 dislikesObject = res.dislikesObject 969 dislikesObject = res.dislikesObject
@@ -1032,6 +1028,7 @@ export class VideoModel extends Model<VideoModel> {
1032 updated: this.updatedAt.toISOString(), 1028 updated: this.updatedAt.toISOString(),
1033 mediaType: 'text/markdown', 1029 mediaType: 'text/markdown',
1034 content: this.getTruncatedDescription(), 1030 content: this.getTruncatedDescription(),
1031 support: this.support,
1035 icon: { 1032 icon: {
1036 type: 'Image', 1033 type: 'Image',
1037 url: this.getThumbnailUrl(baseUrlHttp), 1034 url: this.getThumbnailUrl(baseUrlHttp),
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index d9dea0713..ee591d620 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -255,6 +255,14 @@ describe('Test users API validators', function () {
255 await makePutBodyRequest({ url: server.url, path: path + 'me', token: 'super token', fields, statusCodeExpected: 401 }) 255 await makePutBodyRequest({ url: server.url, path: path + 'me', token: 'super token', fields, statusCodeExpected: 401 })
256 }) 256 })
257 257
258 it('Should fail with a too long description', async function () {
259 const fields = {
260 description: 'super'.repeat(60)
261 }
262
263 await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
264 })
265
258 it('Should succeed with the correct params', async function () { 266 it('Should succeed with the correct params', async function () {
259 const fields = { 267 const fields = {
260 password: 'my super password', 268 password: 'my super password',
diff --git a/server/tests/api/check-params/video-channels.ts b/server/tests/api/check-params/video-channels.ts
index d073e28f0..43c5462ee 100644
--- a/server/tests/api/check-params/video-channels.ts
+++ b/server/tests/api/check-params/video-channels.ts
@@ -62,7 +62,8 @@ describe('Test videos API validator', function () {
62 describe('When adding a video channel', function () { 62 describe('When adding a video channel', function () {
63 const baseCorrectParams = { 63 const baseCorrectParams = {
64 name: 'hello', 64 name: 'hello',
65 description: 'super description' 65 description: 'super description',
66 support: 'super support text'
66 } 67 }
67 68
68 it('Should fail with a non authenticated user', async function () { 69 it('Should fail with a non authenticated user', async function () {
@@ -89,13 +90,18 @@ describe('Test videos API validator', function () {
89 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) 90 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
90 }) 91 })
91 92
93 it('Should fail with a long support text', async function () {
94 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
95 await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
96 })
97
92 it('Should succeed with the correct parameters', async function () { 98 it('Should succeed with the correct parameters', async function () {
93 await makePostBodyRequest({ 99 await makePostBodyRequest({
94 url: server.url, 100 url: server.url,
95 path, 101 path,
96 token: server.accessToken, 102 token: server.accessToken,
97 fields: baseCorrectParams, 103 fields: baseCorrectParams,
98 statusCodeExpected: 204 104 statusCodeExpected: 200
99 }) 105 })
100 }) 106 })
101 }) 107 })
@@ -143,6 +149,11 @@ describe('Test videos API validator', function () {
143 await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields }) 149 await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields })
144 }) 150 })
145 151
152 it('Should fail with a long support text', async function () {
153 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
154 await makePutBodyRequest({ url: server.url, path: path + '/' + videoChannelId, token: server.accessToken, fields })
155 })
156
146 it('Should succeed with the correct parameters', async function () { 157 it('Should succeed with the correct parameters', async function () {
147 await makePutBodyRequest({ 158 await makePutBodyRequest({
148 url: server.url, 159 url: server.url,
diff --git a/server/tests/api/check-params/videos.ts b/server/tests/api/check-params/videos.ts
index aa30b721b..1d5c8543d 100644
--- a/server/tests/api/check-params/videos.ts
+++ b/server/tests/api/check-params/videos.ts
@@ -102,6 +102,7 @@ describe('Test videos API validator', function () {
102 nsfw: false, 102 nsfw: false,
103 commentsEnabled: true, 103 commentsEnabled: true,
104 description: 'my super description', 104 description: 'my super description',
105 support: 'my super support text',
105 tags: [ 'tag1', 'tag2' ], 106 tags: [ 'tag1', 'tag2' ],
106 privacy: VideoPrivacy.PUBLIC, 107 privacy: VideoPrivacy.PUBLIC,
107 channelId 108 channelId
@@ -178,7 +179,14 @@ describe('Test videos API validator', function () {
178 }) 179 })
179 180
180 it('Should fail with a long description', async function () { 181 it('Should fail with a long description', async function () {
181 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) }) 182 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) })
183 const attaches = baseCorrectAttaches
184
185 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
186 })
187
188 it('Should fail with a long support text', async function () {
189 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
182 const attaches = baseCorrectAttaches 190 const attaches = baseCorrectAttaches
183 191
184 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches }) 192 await makeUploadRequest({ url: server.url, path: path + '/upload', token: server.accessToken, fields, attaches })
@@ -417,7 +425,13 @@ describe('Test videos API validator', function () {
417 }) 425 })
418 426
419 it('Should fail with a long description', async function () { 427 it('Should fail with a long description', async function () {
420 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(1500) }) 428 const fields = immutableAssign(baseCorrectParams, { description: 'super'.repeat(2500) })
429
430 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
431 })
432
433 it('Should fail with a long support text', async function () {
434 const fields = immutableAssign(baseCorrectParams, { support: 'super'.repeat(70) })
421 435
422 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields }) 436 await makePutBodyRequest({ url: server.url, path: path + videoId, token: server.accessToken, fields })
423 }) 437 })
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index 46ad4879e..19b843861 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -280,6 +280,7 @@ describe('Test follows', function () {
280 language: 3, 280 language: 3,
281 nsfw: true, 281 nsfw: true,
282 description: 'my super description', 282 description: 'my super description',
283 support: 'my super support text',
283 host: 'localhost:9003', 284 host: 'localhost:9003',
284 account: 'root', 285 account: 'root',
285 isLocal, 286 isLocal,
diff --git a/server/tests/api/server/handle-down.ts b/server/tests/api/server/handle-down.ts
index 4cedeb89e..84153b097 100644
--- a/server/tests/api/server/handle-down.ts
+++ b/server/tests/api/server/handle-down.ts
@@ -36,6 +36,7 @@ describe('Test handle downs', function () {
36 nsfw: true, 36 nsfw: true,
37 privacy: VideoPrivacy.PUBLIC, 37 privacy: VideoPrivacy.PUBLIC,
38 description: 'my super description for server 1', 38 description: 'my super description for server 1',
39 support: 'my super support text for server 1',
39 tags: [ 'tag1p1', 'tag2p1' ], 40 tags: [ 'tag1p1', 'tag2p1' ],
40 fixture: 'video_short1.webm' 41 fixture: 'video_short1.webm'
41 } 42 }
@@ -51,6 +52,7 @@ describe('Test handle downs', function () {
51 language: 9, 52 language: 9,
52 nsfw: true, 53 nsfw: true,
53 description: 'my super description for server 1', 54 description: 'my super description for server 1',
55 support: 'my super support text for server 1',
54 host: 'localhost:9001', 56 host: 'localhost:9001',
55 account: 'root', 57 account: 'root',
56 isLocal: false, 58 isLocal: false,
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index 3c4eaff14..bb458f7a5 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -3,7 +3,10 @@
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { Account } from '../../../../shared/models/actors' 5import { Account } from '../../../../shared/models/actors'
6import { checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, userLogin, wait } from '../../utils' 6import {
7 checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, updateMyUser, userLogin,
8 wait
9} from '../../utils'
7import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index' 10import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index'
8import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts' 11import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts'
9import { setAccessTokensToServers } from '../../utils/users/login' 12import { setAccessTokensToServers } from '../../utils/users/login'
@@ -51,6 +54,22 @@ describe('Test users with multiple servers', function () {
51 await wait(5000) 54 await wait(5000)
52 }) 55 })
53 56
57 it('Should be able to update my description', async function () {
58 this.timeout(10000)
59
60 await updateMyUser({
61 url: servers[0].url,
62 accessToken: servers[0].accessToken,
63 description: 'my super description updated'
64 })
65
66 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
67 user = res.body
68 expect(user.account.description).to.equal('my super description updated')
69
70 await wait(5000)
71 })
72
54 it('Should be able to update my avatar', async function () { 73 it('Should be able to update my avatar', async function () {
55 this.timeout(10000) 74 this.timeout(10000)
56 75
@@ -70,7 +89,7 @@ describe('Test users with multiple servers', function () {
70 await wait(5000) 89 await wait(5000)
71 }) 90 })
72 91
73 it('Should have updated my avatar on other servers too', async function () { 92 it('Should have updated my avatar and my description on other servers too', async function () {
74 for (const server of servers) { 93 for (const server of servers) {
75 const resAccounts = await getAccountsList(server.url, '-createdAt') 94 const resAccounts = await getAccountsList(server.url, '-createdAt')
76 95
@@ -81,6 +100,7 @@ describe('Test users with multiple servers', function () {
81 const rootServer1Get = resAccount.body as Account 100 const rootServer1Get = resAccount.body as Account
82 expect(rootServer1Get.name).to.equal('root') 101 expect(rootServer1Get.name).to.equal('root')
83 expect(rootServer1Get.host).to.equal('localhost:9001') 102 expect(rootServer1Get.host).to.equal('localhost:9001')
103 expect(rootServer1Get.description).to.equal('my super description updated')
84 104
85 await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png') 105 await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png')
86 } 106 }
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index ac167d4f9..c650a74f5 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -172,6 +172,7 @@ describe('Test users', function () {
172 expect(user.videoQuota).to.equal(2 * 1024 * 1024) 172 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
173 expect(user.roleLabel).to.equal('User') 173 expect(user.roleLabel).to.equal('User')
174 expect(user.id).to.be.a('number') 174 expect(user.id).to.be.a('number')
175 expect(user.account.description).to.be.null
175 }) 176 })
176 177
177 it('Should be able to upload a video with this user', async function () { 178 it('Should be able to upload a video with this user', async function () {
@@ -315,6 +316,7 @@ describe('Test users', function () {
315 expect(user.displayNSFW).to.be.ok 316 expect(user.displayNSFW).to.be.ok
316 expect(user.videoQuota).to.equal(2 * 1024 * 1024) 317 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
317 expect(user.id).to.be.a('number') 318 expect(user.id).to.be.a('number')
319 expect(user.account.description).to.be.null
318 }) 320 })
319 321
320 it('Should be able to change the autoPlayVideo attribute', async function () { 322 it('Should be able to change the autoPlayVideo attribute', async function () {
@@ -345,6 +347,7 @@ describe('Test users', function () {
345 expect(user.displayNSFW).to.be.ok 347 expect(user.displayNSFW).to.be.ok
346 expect(user.videoQuota).to.equal(2 * 1024 * 1024) 348 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
347 expect(user.id).to.be.a('number') 349 expect(user.id).to.be.a('number')
350 expect(user.account.description).to.be.null
348 }) 351 })
349 352
350 it('Should be able to update my avatar', async function () { 353 it('Should be able to update my avatar', async function () {
@@ -362,6 +365,24 @@ describe('Test users', function () {
362 await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.png') 365 await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.png')
363 }) 366 })
364 367
368 it('Should be able to update my description', async function () {
369 await updateMyUser({
370 url: server.url,
371 accessToken: accessTokenUser,
372 description: 'my super description updated'
373 })
374
375 const res = await getMyUserInformation(server.url, accessTokenUser)
376 const user = res.body
377
378 expect(user.username).to.equal('user_1')
379 expect(user.email).to.equal('updated@example.com')
380 expect(user.displayNSFW).to.be.ok
381 expect(user.videoQuota).to.equal(2 * 1024 * 1024)
382 expect(user.id).to.be.a('number')
383 expect(user.account.description).to.equal('my super description updated')
384 })
385
365 it('Should be able to update another user', async function () { 386 it('Should be able to update another user', async function () {
366 await updateUser({ 387 await updateUser({
367 url: server.url, 388 url: server.url,
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index 85d158d61..c82ac1348 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -70,6 +70,7 @@ describe('Test multiple servers', function () {
70 language: 9, 70 language: 9,
71 nsfw: true, 71 nsfw: true,
72 description: 'my super description for server 1', 72 description: 'my super description for server 1',
73 support: 'my super support text for server 1',
73 tags: [ 'tag1p1', 'tag2p1' ], 74 tags: [ 'tag1p1', 'tag2p1' ],
74 channelId: videoChannelId, 75 channelId: videoChannelId,
75 fixture: 'video_short1.webm' 76 fixture: 'video_short1.webm'
@@ -88,6 +89,7 @@ describe('Test multiple servers', function () {
88 language: 9, 89 language: 9,
89 nsfw: true, 90 nsfw: true,
90 description: 'my super description for server 1', 91 description: 'my super description for server 1',
92 support: 'my super support text for server 1',
91 host: 'localhost:9001', 93 host: 'localhost:9001',
92 account: 'root', 94 account: 'root',
93 isLocal, 95 isLocal,
@@ -136,6 +138,7 @@ describe('Test multiple servers', function () {
136 language: 11, 138 language: 11,
137 nsfw: true, 139 nsfw: true,
138 description: 'my super description for server 2', 140 description: 'my super description for server 2',
141 support: 'my super support text for server 2',
139 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ], 142 tags: [ 'tag1p2', 'tag2p2', 'tag3p2' ],
140 fixture: 'video_short2.webm', 143 fixture: 'video_short2.webm',
141 thumbnailfile: 'thumbnail.jpg', 144 thumbnailfile: 'thumbnail.jpg',
@@ -156,6 +159,7 @@ describe('Test multiple servers', function () {
156 language: 11, 159 language: 11,
157 nsfw: true, 160 nsfw: true,
158 description: 'my super description for server 2', 161 description: 'my super description for server 2',
162 support: 'my super support text for server 2',
159 host: 'localhost:9002', 163 host: 'localhost:9002',
160 account: 'user1', 164 account: 'user1',
161 isLocal, 165 isLocal,
@@ -211,6 +215,7 @@ describe('Test multiple servers', function () {
211 language: 11, 215 language: 11,
212 nsfw: true, 216 nsfw: true,
213 description: 'my super description for server 3', 217 description: 'my super description for server 3',
218 support: 'my super support text for server 3',
214 tags: [ 'tag1p3' ], 219 tags: [ 'tag1p3' ],
215 fixture: 'video_short3.webm' 220 fixture: 'video_short3.webm'
216 } 221 }
@@ -223,6 +228,7 @@ describe('Test multiple servers', function () {
223 language: 12, 228 language: 12,
224 nsfw: false, 229 nsfw: false,
225 description: 'my super description for server 3-2', 230 description: 'my super description for server 3-2',
231 support: 'my super support text for server 3-2',
226 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ], 232 tags: [ 'tag2p3', 'tag3p3', 'tag4p3' ],
227 fixture: 'video_short.webm' 233 fixture: 'video_short.webm'
228 } 234 }
@@ -257,6 +263,7 @@ describe('Test multiple servers', function () {
257 language: 11, 263 language: 11,
258 nsfw: true, 264 nsfw: true,
259 description: 'my super description for server 3', 265 description: 'my super description for server 3',
266 support: 'my super support text for server 3',
260 host: 'localhost:9003', 267 host: 'localhost:9003',
261 account: 'root', 268 account: 'root',
262 isLocal, 269 isLocal,
@@ -286,6 +293,7 @@ describe('Test multiple servers', function () {
286 language: 12, 293 language: 12,
287 nsfw: false, 294 nsfw: false,
288 description: 'my super description for server 3-2', 295 description: 'my super description for server 3-2',
296 support: 'my super support text for server 3-2',
289 host: 'localhost:9003', 297 host: 'localhost:9003',
290 account: 'root', 298 account: 'root',
291 commentsEnabled: true, 299 commentsEnabled: true,
@@ -525,6 +533,7 @@ describe('Test multiple servers', function () {
525 language: 13, 533 language: 13,
526 nsfw: true, 534 nsfw: true,
527 description: 'my super description updated', 535 description: 'my super description updated',
536 support: 'my super support text updated',
528 tags: [ 'tag_up_1', 'tag_up_2' ], 537 tags: [ 'tag_up_1', 'tag_up_2' ],
529 thumbnailfile: 'thumbnail.jpg', 538 thumbnailfile: 'thumbnail.jpg',
530 previewfile: 'preview.jpg' 539 previewfile: 'preview.jpg'
@@ -553,6 +562,7 @@ describe('Test multiple servers', function () {
553 language: 13, 562 language: 13,
554 nsfw: true, 563 nsfw: true,
555 description: 'my super description updated', 564 description: 'my super description updated',
565 support: 'my super support text updated',
556 host: 'localhost:9003', 566 host: 'localhost:9003',
557 account: 'root', 567 account: 'root',
558 isLocal, 568 isLocal,
@@ -841,6 +851,7 @@ describe('Test multiple servers', function () {
841 language: null, 851 language: null,
842 nsfw: false, 852 nsfw: false,
843 description: null, 853 description: null,
854 support: null,
844 host: 'localhost:9002', 855 host: 'localhost:9002',
845 account: 'root', 856 account: 'root',
846 isLocal, 857 isLocal,
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index 8f55075fb..83b6a0e9a 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -26,6 +26,7 @@ describe('Test a single server', function () {
26 language: 3, 26 language: 3,
27 nsfw: true, 27 nsfw: true,
28 description: 'my super description', 28 description: 'my super description',
29 support: 'my super support text',
29 host: 'localhost:9001', 30 host: 'localhost:9001',
30 account: 'root', 31 account: 'root',
31 isLocal: true, 32 isLocal: true,
@@ -54,6 +55,7 @@ describe('Test a single server', function () {
54 language: 5, 55 language: 5,
55 nsfw: false, 56 nsfw: false,
56 description: 'my super description updated', 57 description: 'my super description updated',
58 support: 'my super support text updated',
57 host: 'localhost:9001', 59 host: 'localhost:9001',
58 account: 'root', 60 account: 'root',
59 isLocal: true, 61 isLocal: true,
diff --git a/server/tests/api/videos/video-channels.ts b/server/tests/api/videos/video-channels.ts
index 25b7ad6ab..b9c9bbf3c 100644
--- a/server/tests/api/videos/video-channels.ts
+++ b/server/tests/api/videos/video-channels.ts
@@ -1,27 +1,27 @@
1/* tslint:disable:no-unused-expression */ 1/* tslint:disable:no-unused-expression */
2 2
3import 'mocha'
4import * as chai from 'chai' 3import * as chai from 'chai'
5const expect = chai.expect 4import 'mocha'
6 5import { User } from '../../../../shared/index'
6import { doubleFollow, flushAndRunMultipleServers, uploadVideo, wait } from '../../utils'
7import { 7import {
8 ServerInfo, 8 addVideoChannel,
9 deleteVideoChannel,
9 flushTests, 10 flushTests,
10 runServer, 11 getAccountVideoChannelsList,
11 setAccessTokensToServers,
12 killallServers,
13 getMyUserInformation, 12 getMyUserInformation,
13 getVideoChannel,
14 getVideoChannelsList, 14 getVideoChannelsList,
15 addVideoChannel, 15 killallServers,
16 getAccountVideoChannelsList, 16 ServerInfo,
17 updateVideoChannel, 17 setAccessTokensToServers,
18 deleteVideoChannel, 18 updateVideoChannel
19 getVideoChannel
20} from '../../utils/index' 19} from '../../utils/index'
21import { User } from '../../../../shared/index'
22 20
23describe('Test a video channels', function () { 21const expect = chai.expect
24 let server: ServerInfo 22
23describe('Test video channels', function () {
24 let servers: ServerInfo[]
25 let userInfo: User 25 let userInfo: User
26 let videoChannelId: number 26 let videoChannelId: number
27 27
@@ -30,29 +30,41 @@ describe('Test a video channels', function () {
30 30
31 await flushTests() 31 await flushTests()
32 32
33 server = await runServer(1) 33 servers = await flushAndRunMultipleServers(2)
34
35 await setAccessTokensToServers(servers)
36 await doubleFollow(servers[0], servers[1])
34 37
35 await setAccessTokensToServers([ server ]) 38 await wait(5000)
36 }) 39 })
37 40
38 it('Should have one video channel (created with root)', async () => { 41 it('Should have one video channel (created with root)', async () => {
39 const res = await getVideoChannelsList(server.url, 0, 2) 42 const res = await getVideoChannelsList(servers[0].url, 0, 2)
40 43
41 expect(res.body.total).to.equal(1) 44 expect(res.body.total).to.equal(1)
42 expect(res.body.data).to.be.an('array') 45 expect(res.body.data).to.be.an('array')
43 expect(res.body.data).to.have.lengthOf(1) 46 expect(res.body.data).to.have.lengthOf(1)
44 }) 47 })
45 48
46 it('Should create another video channel', async () => { 49 it('Should create another video channel', async function () {
50 this.timeout(10000)
51
47 const videoChannel = { 52 const videoChannel = {
48 name: 'second video channel', 53 name: 'second video channel',
49 description: 'super video channel description' 54 description: 'super video channel description',
55 support: 'super video channel support text'
50 } 56 }
51 await addVideoChannel(server.url, server.accessToken, videoChannel) 57 const res = await addVideoChannel(servers[0].url, servers[0].accessToken, videoChannel)
58 videoChannelId = res.body.videoChannel.id
59
60 // The channel is 1 is propagated to servers 2
61 await uploadVideo(servers[0].url, servers[0].accessToken, { channelId: videoChannelId })
62
63 await wait(3000)
52 }) 64 })
53 65
54 it('Should have two video channels when getting my information', async () => { 66 it('Should have two video channels when getting my information', async () => {
55 const res = await getMyUserInformation(server.url, server.accessToken) 67 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
56 userInfo = res.body 68 userInfo = res.body
57 69
58 expect(userInfo.videoChannels).to.be.an('array') 70 expect(userInfo.videoChannels).to.be.an('array')
@@ -62,11 +74,11 @@ describe('Test a video channels', function () {
62 expect(videoChannels[0].displayName).to.equal('Default root channel') 74 expect(videoChannels[0].displayName).to.equal('Default root channel')
63 expect(videoChannels[1].displayName).to.equal('second video channel') 75 expect(videoChannels[1].displayName).to.equal('second video channel')
64 expect(videoChannels[1].description).to.equal('super video channel description') 76 expect(videoChannels[1].description).to.equal('super video channel description')
77 expect(videoChannels[1].support).to.equal('super video channel support text')
65 }) 78 })
66 79
67 it('Should have two video channels when getting account channels', async () => { 80 it('Should have two video channels when getting account channels on server 1', async function () {
68 const res = await getAccountVideoChannelsList(server.url, userInfo.account.uuid) 81 const res = await getAccountVideoChannelsList(servers[0].url, userInfo.account.uuid)
69
70 expect(res.body.total).to.equal(2) 82 expect(res.body.total).to.equal(2)
71 expect(res.body.data).to.be.an('array') 83 expect(res.body.data).to.be.an('array')
72 expect(res.body.data).to.have.lengthOf(2) 84 expect(res.body.data).to.have.lengthOf(2)
@@ -75,12 +87,23 @@ describe('Test a video channels', function () {
75 expect(videoChannels[0].displayName).to.equal('Default root channel') 87 expect(videoChannels[0].displayName).to.equal('Default root channel')
76 expect(videoChannels[1].displayName).to.equal('second video channel') 88 expect(videoChannels[1].displayName).to.equal('second video channel')
77 expect(videoChannels[1].description).to.equal('super video channel description') 89 expect(videoChannels[1].description).to.equal('super video channel description')
90 expect(videoChannels[1].support).to.equal('super video channel support text')
91 })
92
93 it('Should have one video channel when getting account channels on server 2', async function () {
94 const res = await getAccountVideoChannelsList(servers[1].url, userInfo.account.uuid)
95 expect(res.body.total).to.equal(1)
96 expect(res.body.data).to.be.an('array')
97 expect(res.body.data).to.have.lengthOf(1)
78 98
79 videoChannelId = videoChannels[1].id 99 const videoChannels = res.body.data
100 expect(videoChannels[0].displayName).to.equal('second video channel')
101 expect(videoChannels[0].description).to.equal('super video channel description')
102 expect(videoChannels[0].support).to.equal('super video channel support text')
80 }) 103 })
81 104
82 it('Should list video channels', async () => { 105 it('Should list video channels', async function () {
83 const res = await getVideoChannelsList(server.url, 1, 1, '-name') 106 const res = await getVideoChannelsList(servers[0].url, 1, 1, '-name')
84 107
85 expect(res.body.total).to.equal(2) 108 expect(res.body.total).to.equal(2)
86 expect(res.body.data).to.be.an('array') 109 expect(res.body.data).to.be.an('array')
@@ -88,39 +111,48 @@ describe('Test a video channels', function () {
88 expect(res.body.data[0].displayName).to.equal('Default root channel') 111 expect(res.body.data[0].displayName).to.equal('Default root channel')
89 }) 112 })
90 113
91 it('Should update video channel', async () => { 114 it('Should update video channel', async function () {
115 this.timeout(5000)
116
92 const videoChannelAttributes = { 117 const videoChannelAttributes = {
93 name: 'video channel updated', 118 name: 'video channel updated',
94 description: 'video channel description updated' 119 description: 'video channel description updated',
120 support: 'video channel support text updated'
95 } 121 }
96 122
97 await updateVideoChannel(server.url, server.accessToken, videoChannelId, videoChannelAttributes) 123 await updateVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId, videoChannelAttributes)
124
125 await wait(3000)
98 }) 126 })
99 127
100 it('Should have video channel updated', async () => { 128 it('Should have video channel updated', async function () {
101 const res = await getVideoChannelsList(server.url, 0, 1, '-name') 129 for (const server of servers) {
130 const res = await getVideoChannelsList(server.url, 0, 1, '-name')
102 131
103 expect(res.body.total).to.equal(2) 132 expect(res.body.total).to.equal(2)
104 expect(res.body.data).to.be.an('array') 133 expect(res.body.data).to.be.an('array')
105 expect(res.body.data).to.have.lengthOf(1) 134 expect(res.body.data).to.have.lengthOf(1)
106 expect(res.body.data[0].displayName).to.equal('video channel updated') 135 expect(res.body.data[0].displayName).to.equal('video channel updated')
107 expect(res.body.data[0].description).to.equal('video channel description updated') 136 expect(res.body.data[0].description).to.equal('video channel description updated')
137 expect(res.body.data[0].support).to.equal('video channel support text updated')
138 }
108 }) 139 })
109 140
110 it('Should get video channel', async () => { 141 it('Should get video channel', async function () {
111 const res = await getVideoChannel(server.url, videoChannelId) 142 const res = await getVideoChannel(servers[0].url, videoChannelId)
112 143
113 const videoChannel = res.body 144 const videoChannel = res.body
114 expect(videoChannel.displayName).to.equal('video channel updated') 145 expect(videoChannel.displayName).to.equal('video channel updated')
115 expect(videoChannel.description).to.equal('video channel description updated') 146 expect(videoChannel.description).to.equal('video channel description updated')
147 expect(videoChannel.support).to.equal('video channel support text updated')
116 }) 148 })
117 149
118 it('Should delete video channel', async () => { 150 it('Should delete video channel', async function () {
119 await deleteVideoChannel(server.url, server.accessToken, videoChannelId) 151 await deleteVideoChannel(servers[0].url, servers[0].accessToken, videoChannelId)
120 }) 152 })
121 153
122 it('Should have video channel deleted', async () => { 154 it('Should have video channel deleted', async function () {
123 const res = await getVideoChannelsList(server.url, 0, 10) 155 const res = await getVideoChannelsList(servers[0].url, 0, 10)
124 156
125 expect(res.body.total).to.equal(1) 157 expect(res.body.total).to.equal(1)
126 expect(res.body.data).to.be.an('array') 158 expect(res.body.data).to.be.an('array')
@@ -129,7 +161,7 @@ describe('Test a video channels', function () {
129 }) 161 })
130 162
131 after(async function () { 163 after(async function () {
132 killallServers([ server ]) 164 killallServers(servers)
133 165
134 // Keep the logs if the test failed 166 // Keep the logs if the test failed
135 if (this['ok']) { 167 if (this['ok']) {
diff --git a/server/tests/utils/users/users.ts b/server/tests/utils/users/users.ts
index 3c9d46246..daf731a14 100644
--- a/server/tests/utils/users/users.ts
+++ b/server/tests/utils/users/users.ts
@@ -131,6 +131,7 @@ function updateMyUser (options: {
131 displayNSFW?: boolean, 131 displayNSFW?: boolean,
132 email?: string, 132 email?: string,
133 autoPlayVideo?: boolean 133 autoPlayVideo?: boolean
134 description?: string
134}) { 135}) {
135 const path = '/api/v1/users/me' 136 const path = '/api/v1/users/me'
136 137
@@ -139,6 +140,7 @@ function updateMyUser (options: {
139 if (options.displayNSFW !== undefined && options.displayNSFW !== null) toSend['displayNSFW'] = options.displayNSFW 140 if (options.displayNSFW !== undefined && options.displayNSFW !== null) toSend['displayNSFW'] = options.displayNSFW
140 if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo 141 if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
141 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email 142 if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
143 if (options.description !== undefined && options.description !== null) toSend['description'] = options.description
142 144
143 return makePutBodyRequest({ 145 return makePutBodyRequest({
144 url: options.url, 146 url: options.url,
diff --git a/server/tests/utils/videos/video-channels.ts b/server/tests/utils/videos/video-channels.ts
index 062ca8f4d..2d095d8ab 100644
--- a/server/tests/utils/videos/video-channels.ts
+++ b/server/tests/utils/videos/video-channels.ts
@@ -3,6 +3,7 @@ import * as request from 'supertest'
3type VideoChannelAttributes = { 3type VideoChannelAttributes = {
4 name?: string 4 name?: string
5 description?: string 5 description?: string
6 support?: string
6} 7}
7 8
8function getVideoChannelsList (url: string, start: number, count: number, sort?: string) { 9function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
@@ -30,13 +31,14 @@ function getAccountVideoChannelsList (url: string, accountId: number | string, s
30 .expect('Content-Type', /json/) 31 .expect('Content-Type', /json/)
31} 32}
32 33
33function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 204) { 34function addVideoChannel (url: string, token: string, videoChannelAttributesArg: VideoChannelAttributes, expectedStatus = 200) {
34 const path = '/api/v1/videos/channels' 35 const path = '/api/v1/videos/channels'
35 36
36 // Default attributes 37 // Default attributes
37 let attributes = { 38 let attributes = {
38 name: 'my super video channel', 39 name: 'my super video channel',
39 description: 'my super channel description' 40 description: 'my super channel description',
41 support: 'my super channel support'
40 } 42 }
41 attributes = Object.assign(attributes, videoChannelAttributesArg) 43 attributes = Object.assign(attributes, videoChannelAttributesArg)
42 44
@@ -54,6 +56,7 @@ function updateVideoChannel (url: string, token: string, channelId: number, attr
54 56
55 if (attributes.name) body['name'] = attributes.name 57 if (attributes.name) body['name'] = attributes.name
56 if (attributes.description) body['description'] = attributes.description 58 if (attributes.description) body['description'] = attributes.description
59 if (attributes.support) body['support'] = attributes.support
57 60
58 return request(url) 61 return request(url)
59 .put(path) 62 .put(path)
diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts
index 9a4af0b9f..a06078d40 100644
--- a/server/tests/utils/videos/videos.ts
+++ b/server/tests/utils/videos/videos.ts
@@ -248,6 +248,7 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
248 channelId: defaultChannelId, 248 channelId: defaultChannelId,
249 nsfw: true, 249 nsfw: true,
250 description: 'my super description', 250 description: 'my super description',
251 support: 'my super support text',
251 tags: [ 'tag' ], 252 tags: [ 'tag' ],
252 privacy: VideoPrivacy.PUBLIC, 253 privacy: VideoPrivacy.PUBLIC,
253 commentsEnabled: true, 254 commentsEnabled: true,
@@ -277,6 +278,10 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
277 req.field('licence', attributes.licence.toString()) 278 req.field('licence', attributes.licence.toString())
278 } 279 }
279 280
281 for (let i = 0; i < attributes.tags.length; i++) {
282 req.field('tags[' + i + ']', attributes.tags[i])
283 }
284
280 if (attributes.thumbnailfile !== undefined) { 285 if (attributes.thumbnailfile !== undefined) {
281 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile)) 286 req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
282 } 287 }
@@ -284,10 +289,6 @@ async function uploadVideo (url: string, accessToken: string, videoAttributesArg
284 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile)) 289 req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
285 } 290 }
286 291
287 for (let i = 0; i < attributes.tags.length; i++) {
288 req.field('tags[' + i + ']', attributes.tags[i])
289 }
290
291 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture)) 292 return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
292 .expect(specialStatus) 293 .expect(specialStatus)
293} 294}
@@ -366,6 +367,7 @@ async function completeVideoCheck (
366 nsfw: boolean 367 nsfw: boolean
367 commentsEnabled: boolean 368 commentsEnabled: boolean
368 description: string 369 description: string
370 support: string
369 host: string 371 host: string
370 account: string 372 account: string
371 isLocal: boolean, 373 isLocal: boolean,
diff --git a/server/tools/upload.ts b/server/tools/upload.ts
index 3bf9dd65e..de8ff8d26 100644
--- a/server/tools/upload.ts
+++ b/server/tools/upload.ts
@@ -19,6 +19,7 @@ program
19 .option('-L, --language <language number>', 'Language number') 19 .option('-L, --language <language number>', 'Language number')
20 .option('-d, --video-description <description>', 'Video description') 20 .option('-d, --video-description <description>', 'Video description')
21 .option('-t, --tags <tags>', 'Video tags', list) 21 .option('-t, --tags <tags>', 'Video tags', list)
22 .option('-b, --thumbnail <thumbnailPath>', 'Thumbnail path')
22 .option('-f, --file <file>', 'Video absolute file path') 23 .option('-f, --file <file>', 'Video absolute file path')
23 .parse(process.argv) 24 .parse(process.argv)
24 25
@@ -72,7 +73,8 @@ async function run () {
72 description: program['videoDescription'], 73 description: program['videoDescription'],
73 tags: program['tags'], 74 tags: program['tags'],
74 commentsEnabled: program['commentsEnabled'], 75 commentsEnabled: program['commentsEnabled'],
75 fixture: program['file'] 76 fixture: program['file'],
77 thumbnailfile: program['thumbnailPath']
76 } 78 }
77 79
78 await uploadVideo(program['url'], accessToken, videoAttributes) 80 await uploadVideo(program['url'], accessToken, videoAttributes)