diff options
author | Chocobozzz <me@florianbigard.com> | 2021-04-06 17:01:35 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2021-04-08 10:07:53 +0200 |
commit | 2cb03dc1f4e01ba491c36caff30c33fe9c5bad89 (patch) | |
tree | 08a8706d105ea1e280339c02b9e2b1dc1edb0ff9 /server/lib | |
parent | f479685678406a5df864d89615b33d29085ebfc6 (diff) | |
download | PeerTube-2cb03dc1f4e01ba491c36caff30c33fe9c5bad89.tar.gz PeerTube-2cb03dc1f4e01ba491c36caff30c33fe9c5bad89.tar.zst PeerTube-2cb03dc1f4e01ba491c36caff30c33fe9c5bad89.zip |
Add banners support
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/actor.ts | 106 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-delete.ts | 6 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-update.ts | 14 | ||||
-rw-r--r-- | server/lib/actor-image.ts | 51 | ||||
-rw-r--r-- | server/lib/client-html.ts | 6 | ||||
-rw-r--r-- | server/lib/emailer.ts | 2 | ||||
-rw-r--r-- | server/lib/video-channel.ts | 16 |
7 files changed, 129 insertions, 72 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index da831dcfd..fe4796a3d 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -4,6 +4,7 @@ import { Op, Transaction } from 'sequelize' | |||
4 | import { URL } from 'url' | 4 | import { URL } from 'url' |
5 | import { v4 as uuidv4 } from 'uuid' | 5 | import { v4 as uuidv4 } from 'uuid' |
6 | import { getServerActor } from '@server/models/application/application' | 6 | import { getServerActor } from '@server/models/application/application' |
7 | import { ActorImageType } from '@shared/models' | ||
7 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' | 8 | import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' |
8 | import { ActivityPubActor, ActivityPubActorType, ActivityPubOrderedCollection } from '../../../shared/models/activitypub' | 9 | import { ActivityPubActor, ActivityPubActorType, ActivityPubOrderedCollection } from '../../../shared/models/activitypub' |
9 | import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects' | 10 | import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects' |
@@ -30,10 +31,10 @@ import { | |||
30 | MActorAccountChannelId, | 31 | MActorAccountChannelId, |
31 | MActorAccountChannelIdActor, | 32 | MActorAccountChannelIdActor, |
32 | MActorAccountId, | 33 | MActorAccountId, |
33 | MActorDefault, | ||
34 | MActorFull, | 34 | MActorFull, |
35 | MActorFullActor, | 35 | MActorFullActor, |
36 | MActorId, | 36 | MActorId, |
37 | MActorImages, | ||
37 | MChannel | 38 | MChannel |
38 | } from '../../types/models' | 39 | } from '../../types/models' |
39 | import { JobQueue } from '../job-queue' | 40 | import { JobQueue } from '../job-queue' |
@@ -168,43 +169,60 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ | |||
168 | } | 169 | } |
169 | } | 170 | } |
170 | 171 | ||
171 | type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string } | 172 | type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string, type: ActorImageType } |
172 | async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) { | 173 | async function updateActorImageInstance (actor: MActorImages, info: AvatarInfo, t: Transaction) { |
173 | if (!info.name) return actor | 174 | if (!info.name) return actor |
174 | 175 | ||
175 | if (actor.Avatar) { | 176 | const oldImageModel = info.type === ActorImageType.AVATAR |
177 | ? actor.Avatar | ||
178 | : actor.Banner | ||
179 | |||
180 | if (oldImageModel) { | ||
176 | // Don't update the avatar if the file URL did not change | 181 | // Don't update the avatar if the file URL did not change |
177 | if (info.fileUrl && actor.Avatar.fileUrl === info.fileUrl) return actor | 182 | if (info.fileUrl && oldImageModel.fileUrl === info.fileUrl) return actor |
178 | 183 | ||
179 | try { | 184 | try { |
180 | await actor.Avatar.destroy({ transaction: t }) | 185 | await oldImageModel.destroy({ transaction: t }) |
181 | } catch (err) { | 186 | } catch (err) { |
182 | logger.error('Cannot remove old avatar of actor %s.', actor.url, { err }) | 187 | logger.error('Cannot remove old actor image of actor %s.', actor.url, { err }) |
183 | } | 188 | } |
184 | } | 189 | } |
185 | 190 | ||
186 | const avatar = await ActorImageModel.create({ | 191 | const imageModel = await ActorImageModel.create({ |
187 | filename: info.name, | 192 | filename: info.name, |
188 | onDisk: info.onDisk, | 193 | onDisk: info.onDisk, |
189 | fileUrl: info.fileUrl | 194 | fileUrl: info.fileUrl, |
195 | type: info.type | ||
190 | }, { transaction: t }) | 196 | }, { transaction: t }) |
191 | 197 | ||
192 | actor.avatarId = avatar.id | 198 | if (info.type === ActorImageType.AVATAR) { |
193 | actor.Avatar = avatar | 199 | actor.avatarId = imageModel.id |
200 | actor.Avatar = imageModel | ||
201 | } else { | ||
202 | actor.bannerId = imageModel.id | ||
203 | actor.Banner = imageModel | ||
204 | } | ||
194 | 205 | ||
195 | return actor | 206 | return actor |
196 | } | 207 | } |
197 | 208 | ||
198 | async function deleteActorAvatarInstance (actor: MActorDefault, t: Transaction) { | 209 | async function deleteActorImageInstance (actor: MActorImages, type: ActorImageType, t: Transaction) { |
199 | try { | 210 | try { |
200 | await actor.Avatar.destroy({ transaction: t }) | 211 | if (type === ActorImageType.AVATAR) { |
212 | await actor.Avatar.destroy({ transaction: t }) | ||
213 | |||
214 | actor.avatarId = null | ||
215 | actor.Avatar = null | ||
216 | } else { | ||
217 | await actor.Banner.destroy({ transaction: t }) | ||
218 | |||
219 | actor.bannerId = null | ||
220 | actor.Banner = null | ||
221 | } | ||
201 | } catch (err) { | 222 | } catch (err) { |
202 | logger.error('Cannot remove old avatar of actor %s.', actor.url, { err }) | 223 | logger.error('Cannot remove old image of actor %s.', actor.url, { err }) |
203 | } | 224 | } |
204 | 225 | ||
205 | actor.avatarId = null | ||
206 | actor.Avatar = null | ||
207 | |||
208 | return actor | 226 | return actor |
209 | } | 227 | } |
210 | 228 | ||
@@ -219,9 +237,11 @@ async function fetchActorTotalItems (url: string) { | |||
219 | } | 237 | } |
220 | } | 238 | } |
221 | 239 | ||
222 | function getAvatarInfoIfExists (actorJSON: ActivityPubActor) { | 240 | function getImageInfoIfExists (actorJSON: ActivityPubActor, type: ActorImageType) { |
223 | const mimetypes = MIMETYPES.IMAGE | 241 | const mimetypes = MIMETYPES.IMAGE |
224 | const icon = actorJSON.icon | 242 | const icon = type === ActorImageType.AVATAR |
243 | ? actorJSON.icon | ||
244 | : actorJSON.image | ||
225 | 245 | ||
226 | if (!icon || icon.type !== 'Image' || !isActivityPubUrlValid(icon.url)) return undefined | 246 | if (!icon || icon.type !== 'Image' || !isActivityPubUrlValid(icon.url)) return undefined |
227 | 247 | ||
@@ -239,7 +259,8 @@ function getAvatarInfoIfExists (actorJSON: ActivityPubActor) { | |||
239 | 259 | ||
240 | return { | 260 | return { |
241 | name: uuidv4() + extension, | 261 | name: uuidv4() + extension, |
242 | fileUrl: icon.url | 262 | fileUrl: icon.url, |
263 | type | ||
243 | } | 264 | } |
244 | } | 265 | } |
245 | 266 | ||
@@ -293,10 +314,22 @@ async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannel | |||
293 | const avatarInfo = { | 314 | const avatarInfo = { |
294 | name: result.avatar.name, | 315 | name: result.avatar.name, |
295 | fileUrl: result.avatar.fileUrl, | 316 | fileUrl: result.avatar.fileUrl, |
296 | onDisk: false | 317 | onDisk: false, |
318 | type: ActorImageType.AVATAR | ||
319 | } | ||
320 | |||
321 | await updateActorImageInstance(actor, avatarInfo, t) | ||
322 | } | ||
323 | |||
324 | if (result.banner !== undefined) { | ||
325 | const bannerInfo = { | ||
326 | name: result.banner.name, | ||
327 | fileUrl: result.banner.fileUrl, | ||
328 | onDisk: false, | ||
329 | type: ActorImageType.BANNER | ||
297 | } | 330 | } |
298 | 331 | ||
299 | await updateActorAvatarInstance(actor, avatarInfo, t) | 332 | await updateActorImageInstance(actor, bannerInfo, t) |
300 | } | 333 | } |
301 | 334 | ||
302 | // Force update | 335 | // Force update |
@@ -338,11 +371,11 @@ export { | |||
338 | buildActorInstance, | 371 | buildActorInstance, |
339 | generateAndSaveActorKeys, | 372 | generateAndSaveActorKeys, |
340 | fetchActorTotalItems, | 373 | fetchActorTotalItems, |
341 | getAvatarInfoIfExists, | 374 | getImageInfoIfExists, |
342 | updateActorInstance, | 375 | updateActorInstance, |
343 | deleteActorAvatarInstance, | 376 | deleteActorImageInstance, |
344 | refreshActorIfNeeded, | 377 | refreshActorIfNeeded, |
345 | updateActorAvatarInstance, | 378 | updateActorImageInstance, |
346 | addFetchOutboxJob | 379 | addFetchOutboxJob |
347 | } | 380 | } |
348 | 381 | ||
@@ -381,12 +414,25 @@ function saveActorAndServerAndModelIfNotExist ( | |||
381 | const avatar = await ActorImageModel.create({ | 414 | const avatar = await ActorImageModel.create({ |
382 | filename: result.avatar.name, | 415 | filename: result.avatar.name, |
383 | fileUrl: result.avatar.fileUrl, | 416 | fileUrl: result.avatar.fileUrl, |
384 | onDisk: false | 417 | onDisk: false, |
418 | type: ActorImageType.AVATAR | ||
385 | }, { transaction: t }) | 419 | }, { transaction: t }) |
386 | 420 | ||
387 | actor.avatarId = avatar.id | 421 | actor.avatarId = avatar.id |
388 | } | 422 | } |
389 | 423 | ||
424 | // Banner? | ||
425 | if (result.banner) { | ||
426 | const banner = await ActorImageModel.create({ | ||
427 | filename: result.banner.name, | ||
428 | fileUrl: result.banner.fileUrl, | ||
429 | onDisk: false, | ||
430 | type: ActorImageType.BANNER | ||
431 | }, { transaction: t }) | ||
432 | |||
433 | actor.bannerId = banner.id | ||
434 | } | ||
435 | |||
390 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists | 436 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists |
391 | // (which could be false in a retried query) | 437 | // (which could be false in a retried query) |
392 | const [ actorCreated, created ] = await ActorModel.findOrCreate<MActorFullActor>({ | 438 | const [ actorCreated, created ] = await ActorModel.findOrCreate<MActorFullActor>({ |
@@ -440,6 +486,10 @@ type FetchRemoteActorResult = { | |||
440 | name: string | 486 | name: string |
441 | fileUrl: string | 487 | fileUrl: string |
442 | } | 488 | } |
489 | banner?: { | ||
490 | name: string | ||
491 | fileUrl: string | ||
492 | } | ||
443 | attributedTo: ActivityPubAttributedTo[] | 493 | attributedTo: ActivityPubAttributedTo[] |
444 | } | 494 | } |
445 | async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> { | 495 | async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: number, result: FetchRemoteActorResult }> { |
@@ -479,7 +529,8 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
479 | : null | 529 | : null |
480 | }) | 530 | }) |
481 | 531 | ||
482 | const avatarInfo = await getAvatarInfoIfExists(actorJSON) | 532 | const avatarInfo = getImageInfoIfExists(actorJSON, ActorImageType.AVATAR) |
533 | const bannerInfo = getImageInfoIfExists(actorJSON, ActorImageType.BANNER) | ||
483 | 534 | ||
484 | const name = actorJSON.name || actorJSON.preferredUsername | 535 | const name = actorJSON.name || actorJSON.preferredUsername |
485 | return { | 536 | return { |
@@ -488,6 +539,7 @@ async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode?: numbe | |||
488 | actor, | 539 | actor, |
489 | name, | 540 | name, |
490 | avatar: avatarInfo, | 541 | avatar: avatarInfo, |
542 | banner: bannerInfo, | ||
491 | summary: actorJSON.summary, | 543 | summary: actorJSON.summary, |
492 | support: actorJSON.support, | 544 | support: actorJSON.support, |
493 | playlists: actorJSON.playlists, | 545 | playlists: actorJSON.playlists, |
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index a86def936..070ee0f1d 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -7,7 +7,7 @@ import { VideoModel } from '../../../models/video/video' | |||
7 | import { VideoCommentModel } from '../../../models/video/video-comment' | 7 | import { VideoCommentModel } from '../../../models/video/video-comment' |
8 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' | 8 | import { VideoPlaylistModel } from '../../../models/video/video-playlist' |
9 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 9 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
10 | import { MAccountActor, MActor, MActorSignature, MChannelActor, MChannelActorAccountActor, MCommentOwnerVideo } from '../../../types/models' | 10 | import { MAccountActor, MActor, MActorSignature, MChannelActor, MCommentOwnerVideo } from '../../../types/models' |
11 | import { markCommentAsDeleted } from '../../video-comment' | 11 | import { markCommentAsDeleted } from '../../video-comment' |
12 | import { forwardVideoRelatedActivity } from '../send/utils' | 12 | import { forwardVideoRelatedActivity } from '../send/utils' |
13 | 13 | ||
@@ -30,9 +30,7 @@ async function processDeleteActivity (options: APProcessorOptions<ActivityDelete | |||
30 | } else if (byActorFull.type === 'Group') { | 30 | } else if (byActorFull.type === 'Group') { |
31 | if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') | 31 | if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.') |
32 | 32 | ||
33 | const channelToDelete = byActorFull.VideoChannel as MChannelActorAccountActor | 33 | const channelToDelete = Object.assign({}, byActorFull.VideoChannel, { Actor: byActorFull }) |
34 | channelToDelete.Actor = byActorFull | ||
35 | |||
36 | return retryTransactionWrapper(processDeleteVideoChannel, channelToDelete) | 34 | return retryTransactionWrapper(processDeleteVideoChannel, channelToDelete) |
37 | } | 35 | } |
38 | } | 36 | } |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 849f70b94..ad3bb392d 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -6,7 +6,7 @@ import { sequelizeTypescript } from '../../../initializers/database' | |||
6 | import { AccountModel } from '../../../models/account/account' | 6 | import { AccountModel } from '../../../models/account/account' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/activitypub/actor' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 8 | import { VideoChannelModel } from '../../../models/video/video-channel' |
9 | import { getAvatarInfoIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor' | 9 | import { getImageInfoIfExists, updateActorImageInstance, updateActorInstance } from '../actor' |
10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' | 10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' |
11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | 11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' |
12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | 12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' |
@@ -17,6 +17,7 @@ import { createOrUpdateVideoPlaylist } from '../playlist' | |||
17 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' | 17 | import { APProcessorOptions } from '../../../types/activitypub-processor.model' |
18 | import { MActorSignature, MAccountIdActor } from '../../../types/models' | 18 | import { MActorSignature, MAccountIdActor } from '../../../types/models' |
19 | import { isRedundancyAccepted } from '@server/lib/redundancy' | 19 | import { isRedundancyAccepted } from '@server/lib/redundancy' |
20 | import { ActorImageType } from '@shared/models' | ||
20 | 21 | ||
21 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { | 22 | async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate>) { |
22 | const { activity, byActor } = options | 23 | const { activity, byActor } = options |
@@ -119,7 +120,8 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
119 | let accountOrChannelFieldsSave: object | 120 | let accountOrChannelFieldsSave: object |
120 | 121 | ||
121 | // Fetch icon? | 122 | // Fetch icon? |
122 | const avatarInfo = await getAvatarInfoIfExists(actorAttributesToUpdate) | 123 | const avatarInfo = getImageInfoIfExists(actorAttributesToUpdate, ActorImageType.AVATAR) |
124 | const bannerInfo = getImageInfoIfExists(actorAttributesToUpdate, ActorImageType.BANNER) | ||
123 | 125 | ||
124 | try { | 126 | try { |
125 | await sequelizeTypescript.transaction(async t => { | 127 | await sequelizeTypescript.transaction(async t => { |
@@ -132,10 +134,12 @@ async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) | |||
132 | 134 | ||
133 | await updateActorInstance(actor, actorAttributesToUpdate) | 135 | await updateActorInstance(actor, actorAttributesToUpdate) |
134 | 136 | ||
135 | if (avatarInfo !== undefined) { | 137 | for (const imageInfo of [ avatarInfo, bannerInfo ]) { |
136 | const avatarOptions = Object.assign({}, avatarInfo, { onDisk: false }) | 138 | if (!imageInfo) continue |
137 | 139 | ||
138 | await updateActorAvatarInstance(actor, avatarOptions, t) | 140 | const imageOptions = Object.assign({}, imageInfo, { onDisk: false }) |
141 | |||
142 | await updateActorImageInstance(actor, imageOptions, t) | ||
139 | } | 143 | } |
140 | 144 | ||
141 | await actor.save({ transaction: t }) | 145 | await actor.save({ transaction: t }) |
diff --git a/server/lib/actor-image.ts b/server/lib/actor-image.ts index ca7f9658d..59afa93bd 100644 --- a/server/lib/actor-image.ts +++ b/server/lib/actor-image.ts | |||
@@ -3,50 +3,57 @@ import { queue } from 'async' | |||
3 | import * as LRUCache from 'lru-cache' | 3 | import * as LRUCache from 'lru-cache' |
4 | import { extname, join } from 'path' | 4 | import { extname, join } from 'path' |
5 | import { v4 as uuidv4 } from 'uuid' | 5 | import { v4 as uuidv4 } from 'uuid' |
6 | import { ActorImageType } from '@shared/models' | ||
6 | import { retryTransactionWrapper } from '../helpers/database-utils' | 7 | import { retryTransactionWrapper } from '../helpers/database-utils' |
7 | import { processImage } from '../helpers/image-utils' | 8 | import { processImage } from '../helpers/image-utils' |
8 | import { downloadImage } from '../helpers/requests' | 9 | import { downloadImage } from '../helpers/requests' |
9 | import { CONFIG } from '../initializers/config' | 10 | import { CONFIG } from '../initializers/config' |
10 | import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' | 11 | import { ACTOR_IMAGES_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants' |
11 | import { sequelizeTypescript } from '../initializers/database' | 12 | import { sequelizeTypescript } from '../initializers/database' |
12 | import { MAccountDefault, MChannelDefault } from '../types/models' | 13 | import { MAccountDefault, MChannelDefault } from '../types/models' |
13 | import { deleteActorAvatarInstance, updateActorAvatarInstance } from './activitypub/actor' | 14 | import { deleteActorImageInstance, updateActorImageInstance } from './activitypub/actor' |
14 | import { sendUpdateActor } from './activitypub/send' | 15 | import { sendUpdateActor } from './activitypub/send' |
15 | 16 | ||
16 | async function updateLocalActorAvatarFile ( | 17 | async function updateLocalActorImageFile ( |
17 | accountOrChannel: MAccountDefault | MChannelDefault, | 18 | accountOrChannel: MAccountDefault | MChannelDefault, |
18 | avatarPhysicalFile: Express.Multer.File | 19 | imagePhysicalFile: Express.Multer.File, |
20 | type: ActorImageType | ||
19 | ) { | 21 | ) { |
20 | const extension = extname(avatarPhysicalFile.filename) | 22 | const imageSize = type === ActorImageType.AVATAR |
23 | ? ACTOR_IMAGES_SIZE.AVATARS | ||
24 | : ACTOR_IMAGES_SIZE.BANNERS | ||
21 | 25 | ||
22 | const avatarName = uuidv4() + extension | 26 | const extension = extname(imagePhysicalFile.filename) |
23 | const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, avatarName) | 27 | |
24 | await processImage(avatarPhysicalFile.path, destination, AVATARS_SIZE) | 28 | const imageName = uuidv4() + extension |
29 | const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, imageName) | ||
30 | await processImage(imagePhysicalFile.path, destination, imageSize) | ||
25 | 31 | ||
26 | return retryTransactionWrapper(() => { | 32 | return retryTransactionWrapper(() => { |
27 | return sequelizeTypescript.transaction(async t => { | 33 | return sequelizeTypescript.transaction(async t => { |
28 | const avatarInfo = { | 34 | const actorImageInfo = { |
29 | name: avatarName, | 35 | name: imageName, |
30 | fileUrl: null, | 36 | fileUrl: null, |
37 | type, | ||
31 | onDisk: true | 38 | onDisk: true |
32 | } | 39 | } |
33 | 40 | ||
34 | const updatedActor = await updateActorAvatarInstance(accountOrChannel.Actor, avatarInfo, t) | 41 | const updatedActor = await updateActorImageInstance(accountOrChannel.Actor, actorImageInfo, t) |
35 | await updatedActor.save({ transaction: t }) | 42 | await updatedActor.save({ transaction: t }) |
36 | 43 | ||
37 | await sendUpdateActor(accountOrChannel, t) | 44 | await sendUpdateActor(accountOrChannel, t) |
38 | 45 | ||
39 | return updatedActor.Avatar | 46 | return type === ActorImageType.AVATAR |
47 | ? updatedActor.Avatar | ||
48 | : updatedActor.Banner | ||
40 | }) | 49 | }) |
41 | }) | 50 | }) |
42 | } | 51 | } |
43 | 52 | ||
44 | async function deleteLocalActorAvatarFile ( | 53 | async function deleteLocalActorImageFile (accountOrChannel: MAccountDefault | MChannelDefault, type: ActorImageType) { |
45 | accountOrChannel: MAccountDefault | MChannelDefault | ||
46 | ) { | ||
47 | return retryTransactionWrapper(() => { | 54 | return retryTransactionWrapper(() => { |
48 | return sequelizeTypescript.transaction(async t => { | 55 | return sequelizeTypescript.transaction(async t => { |
49 | const updatedActor = await deleteActorAvatarInstance(accountOrChannel.Actor, t) | 56 | const updatedActor = await deleteActorImageInstance(accountOrChannel.Actor, type, t) |
50 | await updatedActor.save({ transaction: t }) | 57 | await updatedActor.save({ transaction: t }) |
51 | 58 | ||
52 | await sendUpdateActor(accountOrChannel, t) | 59 | await sendUpdateActor(accountOrChannel, t) |
@@ -56,10 +63,14 @@ async function deleteLocalActorAvatarFile ( | |||
56 | }) | 63 | }) |
57 | } | 64 | } |
58 | 65 | ||
59 | type DownloadImageQueueTask = { fileUrl: string, filename: string } | 66 | type DownloadImageQueueTask = { fileUrl: string, filename: string, type: ActorImageType } |
60 | 67 | ||
61 | const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => { | 68 | const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => { |
62 | downloadImage(task.fileUrl, CONFIG.STORAGE.ACTOR_IMAGES, task.filename, AVATARS_SIZE) | 69 | const size = task.type === ActorImageType.AVATAR |
70 | ? ACTOR_IMAGES_SIZE.AVATARS | ||
71 | : ACTOR_IMAGES_SIZE.BANNERS | ||
72 | |||
73 | downloadImage(task.fileUrl, CONFIG.STORAGE.ACTOR_IMAGES, task.filename, size) | ||
63 | .then(() => cb()) | 74 | .then(() => cb()) |
64 | .catch(err => cb(err)) | 75 | .catch(err => cb(err)) |
65 | }, QUEUE_CONCURRENCY.ACTOR_PROCESS_IMAGE) | 76 | }, QUEUE_CONCURRENCY.ACTOR_PROCESS_IMAGE) |
@@ -79,7 +90,7 @@ const actorImagePathUnsafeCache = new LRUCache<string, string>({ max: LRU_CACHE. | |||
79 | 90 | ||
80 | export { | 91 | export { |
81 | actorImagePathUnsafeCache, | 92 | actorImagePathUnsafeCache, |
82 | updateLocalActorAvatarFile, | 93 | updateLocalActorImageFile, |
83 | deleteLocalActorAvatarFile, | 94 | deleteLocalActorImageFile, |
84 | pushActorImageProcessInQueue | 95 | pushActorImageProcessInQueue |
85 | } | 96 | } |
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts index fcc11c7b2..6ddaa82c8 100644 --- a/server/lib/client-html.ts +++ b/server/lib/client-html.ts | |||
@@ -11,7 +11,7 @@ import { logger } from '../helpers/logger' | |||
11 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../initializers/config' |
12 | import { | 12 | import { |
13 | ACCEPT_HEADERS, | 13 | ACCEPT_HEADERS, |
14 | AVATARS_SIZE, | 14 | ACTOR_IMAGES_SIZE, |
15 | CUSTOM_HTML_TAG_COMMENTS, | 15 | CUSTOM_HTML_TAG_COMMENTS, |
16 | EMBED_SIZE, | 16 | EMBED_SIZE, |
17 | FILES_CONTENT_HASH, | 17 | FILES_CONTENT_HASH, |
@@ -246,8 +246,8 @@ class ClientHtml { | |||
246 | 246 | ||
247 | const image = { | 247 | const image = { |
248 | url: entity.Actor.getAvatarUrl(), | 248 | url: entity.Actor.getAvatarUrl(), |
249 | width: AVATARS_SIZE.width, | 249 | width: ACTOR_IMAGES_SIZE.AVATARS.width, |
250 | height: AVATARS_SIZE.height | 250 | height: ACTOR_IMAGES_SIZE.AVATARS.height |
251 | } | 251 | } |
252 | 252 | ||
253 | const ogType = 'website' | 253 | const ogType = 'website' |
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index ce4134d59..9ca0d5d5b 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -405,7 +405,7 @@ class Emailer { | |||
405 | async addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) { | 405 | async addVideoAutoBlacklistModeratorsNotification (to: string[], videoBlacklist: MVideoBlacklistLightVideo) { |
406 | const videoAutoBlacklistUrl = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' | 406 | const videoAutoBlacklistUrl = WEBSERVER.URL + '/admin/moderation/video-auto-blacklist/list' |
407 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() | 407 | const videoUrl = WEBSERVER.URL + videoBlacklist.Video.getWatchStaticPath() |
408 | const channel = (await VideoChannelModel.loadByIdAndPopulateAccount(videoBlacklist.Video.channelId)).toFormattedSummaryJSON() | 408 | const channel = (await VideoChannelModel.loadAndPopulateAccount(videoBlacklist.Video.channelId)).toFormattedSummaryJSON() |
409 | 409 | ||
410 | const emailPayload: EmailPayload = { | 410 | const emailPayload: EmailPayload = { |
411 | template: 'video-auto-blacklist-new', | 411 | template: 'video-auto-blacklist-new', |
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 49bdf4869..0476cb2d5 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -3,18 +3,12 @@ import { v4 as uuidv4 } from 'uuid' | |||
3 | import { VideoChannelCreate } from '../../shared/models' | 3 | import { VideoChannelCreate } from '../../shared/models' |
4 | import { VideoModel } from '../models/video/video' | 4 | import { VideoModel } from '../models/video/video' |
5 | import { VideoChannelModel } from '../models/video/video-channel' | 5 | import { VideoChannelModel } from '../models/video/video-channel' |
6 | import { MAccountId, MChannelDefault, MChannelId } from '../types/models' | 6 | import { MAccountId, MChannelId } from '../types/models' |
7 | import { buildActorInstance } from './activitypub/actor' | 7 | import { buildActorInstance } from './activitypub/actor' |
8 | import { getLocalVideoChannelActivityPubUrl } from './activitypub/url' | 8 | import { getLocalVideoChannelActivityPubUrl } from './activitypub/url' |
9 | import { federateVideoIfNeeded } from './activitypub/videos' | 9 | import { federateVideoIfNeeded } from './activitypub/videos' |
10 | 10 | ||
11 | type CustomVideoChannelModelAccount <T extends MAccountId> = MChannelDefault & { Account?: T } | 11 | async function createLocalVideoChannel (videoChannelInfo: VideoChannelCreate, account: MAccountId, t: Sequelize.Transaction) { |
12 | |||
13 | async function createLocalVideoChannel <T extends MAccountId> ( | ||
14 | videoChannelInfo: VideoChannelCreate, | ||
15 | account: T, | ||
16 | t: Sequelize.Transaction | ||
17 | ): Promise<CustomVideoChannelModelAccount<T>> { | ||
18 | const uuid = uuidv4() | 12 | const uuid = uuidv4() |
19 | const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) | 13 | const url = getLocalVideoChannelActivityPubUrl(videoChannelInfo.name) |
20 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) | 14 | const actorInstance = buildActorInstance('Group', url, videoChannelInfo.name, uuid) |
@@ -32,13 +26,11 @@ async function createLocalVideoChannel <T extends MAccountId> ( | |||
32 | const videoChannel = new VideoChannelModel(videoChannelData) | 26 | const videoChannel = new VideoChannelModel(videoChannelData) |
33 | 27 | ||
34 | const options = { transaction: t } | 28 | const options = { transaction: t } |
35 | const videoChannelCreated: CustomVideoChannelModelAccount<T> = await videoChannel.save(options) as MChannelDefault | 29 | const videoChannelCreated = await videoChannel.save(options) |
36 | 30 | ||
37 | // Do not forget to add Account/Actor information to the created video channel | ||
38 | videoChannelCreated.Account = account | ||
39 | videoChannelCreated.Actor = actorInstanceCreated | 31 | videoChannelCreated.Actor = actorInstanceCreated |
40 | 32 | ||
41 | // No need to seed this empty video channel to followers | 33 | // No need to send this empty video channel to followers |
42 | return videoChannelCreated | 34 | return videoChannelCreated |
43 | } | 35 | } |
44 | 36 | ||