aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authorkontrollanten <6680299+kontrollanten@users.noreply.github.com>2022-02-28 08:34:43 +0100
committerGitHub <noreply@github.com>2022-02-28 08:34:43 +0100
commitd0800f7661f13fabe7bb6f4aa0ea50764f106405 (patch)
treed43e6b0b6f4a5a32e03487e6464edbcaf288be2a /server/lib
parent5cad2ca9db9b9d138f8a33058d10b94a9fd50c69 (diff)
downloadPeerTube-d0800f7661f13fabe7bb6f4aa0ea50764f106405.tar.gz
PeerTube-d0800f7661f13fabe7bb6f4aa0ea50764f106405.tar.zst
PeerTube-d0800f7661f13fabe7bb6f4aa0ea50764f106405.zip
Implement avatar miniatures (#4639)
* client: remove unused file * refactor(client/my-actor-avatar): size from input Read size from component input instead of scss, to make it possible to use smaller avatar images when implemented. * implement avatar miniatures close #4560 * fix(test): max file size * fix(search-index): normalize res acc to avatarMini * refactor avatars to an array * client/search: resize channel avatar to 120 * refactor(client/videos): remove unused function * client(actor-avatar): set default size * fix tests and avatars full result When findOne is used only an array containting one avatar is returned. * update migration version and version notations * server/search: harmonize normalizing * Cleanup avatar miniature PR Co-authored-by: Chocobozzz <me@florianbigard.com>
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/actors/image.ts89
-rw-r--r--server/lib/activitypub/actors/shared/creator.ts16
-rw-r--r--server/lib/activitypub/actors/shared/object-to-model-attributes.ts56
-rw-r--r--server/lib/activitypub/actors/updater.ts12
-rw-r--r--server/lib/actor-image.ts14
-rw-r--r--server/lib/client-html.ts10
-rw-r--r--server/lib/local-actor.ts89
-rw-r--r--server/lib/notifier/shared/comment/comment-mention.ts2
-rw-r--r--server/lib/notifier/shared/comment/new-comment-for-video-owner.ts2
9 files changed, 165 insertions, 125 deletions
diff --git a/server/lib/activitypub/actors/image.ts b/server/lib/activitypub/actors/image.ts
index 443ad0a63..d17c2ef1a 100644
--- a/server/lib/activitypub/actors/image.ts
+++ b/server/lib/activitypub/actors/image.ts
@@ -12,53 +12,52 @@ type ImageInfo = {
12 onDisk?: boolean 12 onDisk?: boolean
13} 13}
14 14
15async function updateActorImageInstance (actor: MActorImages, type: ActorImageType, imageInfo: ImageInfo | null, t: Transaction) { 15async function updateActorImages (actor: MActorImages, type: ActorImageType, imagesInfo: ImageInfo[], t: Transaction) {
16 const oldImageModel = type === ActorImageType.AVATAR 16 const avatarsOrBanners = type === ActorImageType.AVATAR
17 ? actor.Avatar 17 ? actor.Avatars
18 : actor.Banner 18 : actor.Banners
19 19
20 if (oldImageModel) { 20 if (imagesInfo.length === 0) {
21 // Don't update the avatar if the file URL did not change 21 await deleteActorImages(actor, type, t)
22 if (imageInfo?.fileUrl && oldImageModel.fileUrl === imageInfo.fileUrl) return actor 22 }
23
24 for (const imageInfo of imagesInfo) {
25 const oldImageModel = (avatarsOrBanners || []).find(i => i.width === imageInfo.width)
23 26
24 try { 27 if (oldImageModel) {
25 await oldImageModel.destroy({ transaction: t }) 28 // Don't update the avatar if the file URL did not change
29 if (imageInfo?.fileUrl && oldImageModel.fileUrl === imageInfo.fileUrl) {
30 continue
31 }
26 32
27 setActorImage(actor, type, null) 33 await safeDeleteActorImage(actor, oldImageModel, type, t)
28 } catch (err) {
29 logger.error('Cannot remove old actor image of actor %s.', actor.url, { err })
30 } 34 }
31 }
32 35
33 if (imageInfo) {
34 const imageModel = await ActorImageModel.create({ 36 const imageModel = await ActorImageModel.create({
35 filename: imageInfo.name, 37 filename: imageInfo.name,
36 onDisk: imageInfo.onDisk ?? false, 38 onDisk: imageInfo.onDisk ?? false,
37 fileUrl: imageInfo.fileUrl, 39 fileUrl: imageInfo.fileUrl,
38 height: imageInfo.height, 40 height: imageInfo.height,
39 width: imageInfo.width, 41 width: imageInfo.width,
40 type 42 type,
43 actorId: actor.id
41 }, { transaction: t }) 44 }, { transaction: t })
42 45
43 setActorImage(actor, type, imageModel) 46 addActorImage(actor, type, imageModel)
44 } 47 }
45 48
46 return actor 49 return actor
47} 50}
48 51
49async function deleteActorImageInstance (actor: MActorImages, type: ActorImageType, t: Transaction) { 52async function deleteActorImages (actor: MActorImages, type: ActorImageType, t: Transaction) {
50 try { 53 try {
51 if (type === ActorImageType.AVATAR) { 54 const association = buildAssociationName(type)
52 await actor.Avatar.destroy({ transaction: t })
53
54 actor.avatarId = null
55 actor.Avatar = null
56 } else {
57 await actor.Banner.destroy({ transaction: t })
58 55
59 actor.bannerId = null 56 for (const image of actor[association]) {
60 actor.Banner = null 57 await image.destroy({ transaction: t })
61 } 58 }
59
60 actor[association] = []
62 } catch (err) { 61 } catch (err) {
63 logger.error('Cannot remove old image of actor %s.', actor.url, { err }) 62 logger.error('Cannot remove old image of actor %s.', actor.url, { err })
64 } 63 }
@@ -66,29 +65,37 @@ async function deleteActorImageInstance (actor: MActorImages, type: ActorImageTy
66 return actor 65 return actor
67} 66}
68 67
68async function safeDeleteActorImage (actor: MActorImages, toDelete: MActorImage, type: ActorImageType, t: Transaction) {
69 try {
70 await toDelete.destroy({ transaction: t })
71
72 const association = buildAssociationName(type)
73 actor[association] = actor[association].filter(image => image.id !== toDelete.id)
74 } catch (err) {
75 logger.error('Cannot remove old actor image of actor %s.', actor.url, { err })
76 }
77}
78
69// --------------------------------------------------------------------------- 79// ---------------------------------------------------------------------------
70 80
71export { 81export {
72 ImageInfo, 82 ImageInfo,
73 83
74 updateActorImageInstance, 84 updateActorImages,
75 deleteActorImageInstance 85 deleteActorImages
76} 86}
77 87
78// --------------------------------------------------------------------------- 88// ---------------------------------------------------------------------------
79 89
80function setActorImage (actorModel: MActorImages, type: ActorImageType, imageModel: MActorImage) { 90function addActorImage (actor: MActorImages, type: ActorImageType, imageModel: MActorImage) {
81 const id = imageModel 91 const association = buildAssociationName(type)
82 ? imageModel.id 92 if (!actor[association]) actor[association] = []
83 : null 93
84 94 actor[association].push(imageModel)
85 if (type === ActorImageType.AVATAR) { 95}
86 actorModel.avatarId = id
87 actorModel.Avatar = imageModel
88 } else {
89 actorModel.bannerId = id
90 actorModel.Banner = imageModel
91 }
92 96
93 return actorModel 97function buildAssociationName (type: ActorImageType) {
98 return type === ActorImageType.AVATAR
99 ? 'Avatars'
100 : 'Banners'
94} 101}
diff --git a/server/lib/activitypub/actors/shared/creator.ts b/server/lib/activitypub/actors/shared/creator.ts
index 999aed97d..500bc9912 100644
--- a/server/lib/activitypub/actors/shared/creator.ts
+++ b/server/lib/activitypub/actors/shared/creator.ts
@@ -6,8 +6,8 @@ import { ServerModel } from '@server/models/server/server'
6import { VideoChannelModel } from '@server/models/video/video-channel' 6import { VideoChannelModel } from '@server/models/video/video-channel'
7import { MAccount, MAccountDefault, MActor, MActorFullActor, MActorId, MActorImages, MChannel, MServer } from '@server/types/models' 7import { MAccount, MAccountDefault, MActor, MActorFullActor, MActorId, MActorImages, MChannel, MServer } from '@server/types/models'
8import { ActivityPubActor, ActorImageType } from '@shared/models' 8import { ActivityPubActor, ActorImageType } from '@shared/models'
9import { updateActorImageInstance } from '../image' 9import { updateActorImages } from '../image'
10import { getActorAttributesFromObject, getActorDisplayNameFromObject, getImageInfoFromObject } from './object-to-model-attributes' 10import { getActorAttributesFromObject, getActorDisplayNameFromObject, getImagesInfoFromObject } from './object-to-model-attributes'
11import { fetchActorFollowsCount } from './url-to-object' 11import { fetchActorFollowsCount } from './url-to-object'
12 12
13export class APActorCreator { 13export class APActorCreator {
@@ -27,11 +27,11 @@ export class APActorCreator {
27 return sequelizeTypescript.transaction(async t => { 27 return sequelizeTypescript.transaction(async t => {
28 const server = await this.setServer(actorInstance, t) 28 const server = await this.setServer(actorInstance, t)
29 29
30 await this.setImageIfNeeded(actorInstance, ActorImageType.AVATAR, t)
31 await this.setImageIfNeeded(actorInstance, ActorImageType.BANNER, t)
32
33 const { actorCreated, created } = await this.saveActor(actorInstance, t) 30 const { actorCreated, created } = await this.saveActor(actorInstance, t)
34 31
32 await this.setImageIfNeeded(actorCreated, ActorImageType.AVATAR, t)
33 await this.setImageIfNeeded(actorCreated, ActorImageType.BANNER, t)
34
35 await this.tryToFixActorUrlIfNeeded(actorCreated, actorInstance, created, t) 35 await this.tryToFixActorUrlIfNeeded(actorCreated, actorInstance, created, t)
36 36
37 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { // Account or PeerTube instance 37 if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { // Account or PeerTube instance
@@ -71,10 +71,10 @@ export class APActorCreator {
71 } 71 }
72 72
73 private async setImageIfNeeded (actor: MActor, type: ActorImageType, t: Transaction) { 73 private async setImageIfNeeded (actor: MActor, type: ActorImageType, t: Transaction) {
74 const imageInfo = getImageInfoFromObject(this.actorObject, type) 74 const imagesInfo = getImagesInfoFromObject(this.actorObject, type)
75 if (!imageInfo) return 75 if (imagesInfo.length === 0) return
76 76
77 return updateActorImageInstance(actor as MActorImages, type, imageInfo, t) 77 return updateActorImages(actor as MActorImages, type, imagesInfo, t)
78 } 78 }
79 79
80 private async saveActor (actor: MActor, t: Transaction) { 80 private async saveActor (actor: MActor, t: Transaction) {
diff --git a/server/lib/activitypub/actors/shared/object-to-model-attributes.ts b/server/lib/activitypub/actors/shared/object-to-model-attributes.ts
index 23bc972e5..f6a78c457 100644
--- a/server/lib/activitypub/actors/shared/object-to-model-attributes.ts
+++ b/server/lib/activitypub/actors/shared/object-to-model-attributes.ts
@@ -4,7 +4,7 @@ import { ActorModel } from '@server/models/actor/actor'
4import { FilteredModelAttributes } from '@server/types' 4import { FilteredModelAttributes } from '@server/types'
5import { getLowercaseExtension } from '@shared/core-utils' 5import { getLowercaseExtension } from '@shared/core-utils'
6import { buildUUID } from '@shared/extra-utils' 6import { buildUUID } from '@shared/extra-utils'
7import { ActivityPubActor, ActorImageType } from '@shared/models' 7import { ActivityIconObject, ActivityPubActor, ActorImageType } from '@shared/models'
8 8
9function getActorAttributesFromObject ( 9function getActorAttributesFromObject (
10 actorObject: ActivityPubActor, 10 actorObject: ActivityPubActor,
@@ -30,33 +30,36 @@ function getActorAttributesFromObject (
30 } 30 }
31} 31}
32 32
33function getImageInfoFromObject (actorObject: ActivityPubActor, type: ActorImageType) { 33function getImagesInfoFromObject (actorObject: ActivityPubActor, type: ActorImageType) {
34 const mimetypes = MIMETYPES.IMAGE 34 const iconsOrImages = type === ActorImageType.AVATAR
35 const icon = type === ActorImageType.AVATAR 35 ? actorObject.icons || actorObject.icon
36 ? actorObject.icon
37 : actorObject.image 36 : actorObject.image
38 37
39 if (!icon || icon.type !== 'Image' || !isActivityPubUrlValid(icon.url)) return undefined 38 return normalizeIconOrImage(iconsOrImages).map(iconOrImage => {
39 const mimetypes = MIMETYPES.IMAGE
40 40
41 let extension: string 41 if (iconOrImage.type !== 'Image' || !isActivityPubUrlValid(iconOrImage.url)) return undefined
42 42
43 if (icon.mediaType) { 43 let extension: string
44 extension = mimetypes.MIMETYPE_EXT[icon.mediaType]
45 } else {
46 const tmp = getLowercaseExtension(icon.url)
47 44
48 if (mimetypes.EXT_MIMETYPE[tmp] !== undefined) extension = tmp 45 if (iconOrImage.mediaType) {
49 } 46 extension = mimetypes.MIMETYPE_EXT[iconOrImage.mediaType]
47 } else {
48 const tmp = getLowercaseExtension(iconOrImage.url)
50 49
51 if (!extension) return undefined 50 if (mimetypes.EXT_MIMETYPE[tmp] !== undefined) extension = tmp
51 }
52 52
53 return { 53 if (!extension) return undefined
54 name: buildUUID() + extension, 54
55 fileUrl: icon.url, 55 return {
56 height: icon.height, 56 name: buildUUID() + extension,
57 width: icon.width, 57 fileUrl: iconOrImage.url,
58 type 58 height: iconOrImage.height,
59 } 59 width: iconOrImage.width,
60 type
61 }
62 })
60} 63}
61 64
62function getActorDisplayNameFromObject (actorObject: ActivityPubActor) { 65function getActorDisplayNameFromObject (actorObject: ActivityPubActor) {
@@ -65,6 +68,15 @@ function getActorDisplayNameFromObject (actorObject: ActivityPubActor) {
65 68
66export { 69export {
67 getActorAttributesFromObject, 70 getActorAttributesFromObject,
68 getImageInfoFromObject, 71 getImagesInfoFromObject,
69 getActorDisplayNameFromObject 72 getActorDisplayNameFromObject
70} 73}
74
75// ---------------------------------------------------------------------------
76
77function normalizeIconOrImage (icon: ActivityIconObject | ActivityIconObject[]): ActivityIconObject[] {
78 if (Array.isArray(icon)) return icon
79 if (icon) return [ icon ]
80
81 return []
82}
diff --git a/server/lib/activitypub/actors/updater.ts b/server/lib/activitypub/actors/updater.ts
index 042438d9c..fe94af9f1 100644
--- a/server/lib/activitypub/actors/updater.ts
+++ b/server/lib/activitypub/actors/updater.ts
@@ -5,9 +5,9 @@ import { VideoChannelModel } from '@server/models/video/video-channel'
5import { MAccount, MActor, MActorFull, MChannel } from '@server/types/models' 5import { MAccount, MActor, MActorFull, MChannel } from '@server/types/models'
6import { ActivityPubActor, ActorImageType } from '@shared/models' 6import { ActivityPubActor, ActorImageType } from '@shared/models'
7import { getOrCreateAPOwner } from './get' 7import { getOrCreateAPOwner } from './get'
8import { updateActorImageInstance } from './image' 8import { updateActorImages } from './image'
9import { fetchActorFollowsCount } from './shared' 9import { fetchActorFollowsCount } from './shared'
10import { getImageInfoFromObject } from './shared/object-to-model-attributes' 10import { getImagesInfoFromObject } from './shared/object-to-model-attributes'
11 11
12export class APActorUpdater { 12export class APActorUpdater {
13 13
@@ -29,8 +29,8 @@ export class APActorUpdater {
29 } 29 }
30 30
31 async update () { 31 async update () {
32 const avatarInfo = getImageInfoFromObject(this.actorObject, ActorImageType.AVATAR) 32 const avatarsInfo = getImagesInfoFromObject(this.actorObject, ActorImageType.AVATAR)
33 const bannerInfo = getImageInfoFromObject(this.actorObject, ActorImageType.BANNER) 33 const bannersInfo = getImagesInfoFromObject(this.actorObject, ActorImageType.BANNER)
34 34
35 try { 35 try {
36 await this.updateActorInstance(this.actor, this.actorObject) 36 await this.updateActorInstance(this.actor, this.actorObject)
@@ -47,8 +47,8 @@ export class APActorUpdater {
47 } 47 }
48 48
49 await runInReadCommittedTransaction(async t => { 49 await runInReadCommittedTransaction(async t => {
50 await updateActorImageInstance(this.actor, ActorImageType.AVATAR, avatarInfo, t) 50 await updateActorImages(this.actor, ActorImageType.BANNER, bannersInfo, t)
51 await updateActorImageInstance(this.actor, ActorImageType.BANNER, bannerInfo, t) 51 await updateActorImages(this.actor, ActorImageType.AVATAR, avatarsInfo, t)
52 }) 52 })
53 53
54 await runInReadCommittedTransaction(async t => { 54 await runInReadCommittedTransaction(async t => {
diff --git a/server/lib/actor-image.ts b/server/lib/actor-image.ts
new file mode 100644
index 000000000..e9bd148f6
--- /dev/null
+++ b/server/lib/actor-image.ts
@@ -0,0 +1,14 @@
1import maxBy from 'lodash/maxBy'
2
3function getBiggestActorImage <T extends { width: number }> (images: T[]) {
4 const image = maxBy(images, 'width')
5
6 // If width is null, maxBy won't return a value
7 if (!image) return images[0]
8
9 return image
10}
11
12export {
13 getBiggestActorImage
14}
diff --git a/server/lib/client-html.ts b/server/lib/client-html.ts
index 19354ab70..c010f3c44 100644
--- a/server/lib/client-html.ts
+++ b/server/lib/client-html.ts
@@ -3,6 +3,7 @@ import { readFile } from 'fs-extra'
3import { join } from 'path' 3import { join } from 'path'
4import validator from 'validator' 4import validator from 'validator'
5import { toCompleteUUID } from '@server/helpers/custom-validators/misc' 5import { toCompleteUUID } from '@server/helpers/custom-validators/misc'
6import { ActorImageModel } from '@server/models/actor/actor-image'
6import { root } from '@shared/core-utils' 7import { root } from '@shared/core-utils'
7import { escapeHTML } from '@shared/core-utils/renderer' 8import { escapeHTML } from '@shared/core-utils/renderer'
8import { sha256 } from '@shared/extra-utils' 9import { sha256 } from '@shared/extra-utils'
@@ -16,7 +17,6 @@ import { mdToOneLinePlainText } from '../helpers/markdown'
16import { CONFIG } from '../initializers/config' 17import { CONFIG } from '../initializers/config'
17import { 18import {
18 ACCEPT_HEADERS, 19 ACCEPT_HEADERS,
19 ACTOR_IMAGES_SIZE,
20 CUSTOM_HTML_TAG_COMMENTS, 20 CUSTOM_HTML_TAG_COMMENTS,
21 EMBED_SIZE, 21 EMBED_SIZE,
22 FILES_CONTENT_HASH, 22 FILES_CONTENT_HASH,
@@ -29,6 +29,7 @@ import { VideoModel } from '../models/video/video'
29import { VideoChannelModel } from '../models/video/video-channel' 29import { VideoChannelModel } from '../models/video/video-channel'
30import { VideoPlaylistModel } from '../models/video/video-playlist' 30import { VideoPlaylistModel } from '../models/video/video-playlist'
31import { MAccountActor, MChannelActor } from '../types/models' 31import { MAccountActor, MChannelActor } from '../types/models'
32import { getBiggestActorImage } from './actor-image'
32import { ServerConfigManager } from './server-config-manager' 33import { ServerConfigManager } from './server-config-manager'
33 34
34type Tags = { 35type Tags = {
@@ -273,10 +274,11 @@ class ClientHtml {
273 const siteName = CONFIG.INSTANCE.NAME 274 const siteName = CONFIG.INSTANCE.NAME
274 const title = entity.getDisplayName() 275 const title = entity.getDisplayName()
275 276
277 const avatar = getBiggestActorImage(entity.Actor.Avatars)
276 const image = { 278 const image = {
277 url: entity.Actor.getAvatarUrl(), 279 url: ActorImageModel.getImageUrl(avatar),
278 width: ACTOR_IMAGES_SIZE.AVATARS.width, 280 width: avatar?.width,
279 height: ACTOR_IMAGES_SIZE.AVATARS.height 281 height: avatar?.height
280 } 282 }
281 283
282 const ogType = 'website' 284 const ogType = 'website'
diff --git a/server/lib/local-actor.ts b/server/lib/local-actor.ts
index c6826759b..01046d017 100644
--- a/server/lib/local-actor.ts
+++ b/server/lib/local-actor.ts
@@ -1,5 +1,5 @@
1import 'multer'
2import { queue } from 'async' 1import { queue } from 'async'
2import { remove } from 'fs-extra'
3import LRUCache from 'lru-cache' 3import LRUCache from 'lru-cache'
4import { join } from 'path' 4import { join } from 'path'
5import { ActorModel } from '@server/models/actor/actor' 5import { ActorModel } from '@server/models/actor/actor'
@@ -13,7 +13,7 @@ import { CONFIG } from '../initializers/config'
13import { ACTOR_IMAGES_SIZE, LRU_CACHE, QUEUE_CONCURRENCY, WEBSERVER } from '../initializers/constants' 13import { ACTOR_IMAGES_SIZE, LRU_CACHE, QUEUE_CONCURRENCY, WEBSERVER } from '../initializers/constants'
14import { sequelizeTypescript } from '../initializers/database' 14import { sequelizeTypescript } from '../initializers/database'
15import { MAccountDefault, MActor, MChannelDefault } from '../types/models' 15import { MAccountDefault, MActor, MChannelDefault } from '../types/models'
16import { deleteActorImageInstance, updateActorImageInstance } from './activitypub/actors' 16import { deleteActorImages, updateActorImages } from './activitypub/actors'
17import { sendUpdateActor } from './activitypub/send' 17import { sendUpdateActor } from './activitypub/send'
18 18
19function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) { 19function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) {
@@ -33,64 +33,69 @@ function buildActorInstance (type: ActivityPubActorType, url: string, preferredU
33 }) as MActor 33 }) as MActor
34} 34}
35 35
36async function updateLocalActorImageFile ( 36async function updateLocalActorImageFiles (
37 accountOrChannel: MAccountDefault | MChannelDefault, 37 accountOrChannel: MAccountDefault | MChannelDefault,
38 imagePhysicalFile: Express.Multer.File, 38 imagePhysicalFile: Express.Multer.File,
39 type: ActorImageType 39 type: ActorImageType
40) { 40) {
41 const imageSize = type === ActorImageType.AVATAR 41 const processImageSize = async (imageSize: { width: number, height: number }) => {
42 ? ACTOR_IMAGES_SIZE.AVATARS 42 const extension = getLowercaseExtension(imagePhysicalFile.filename)
43 : ACTOR_IMAGES_SIZE.BANNERS 43
44 44 const imageName = buildUUID() + extension
45 const extension = getLowercaseExtension(imagePhysicalFile.filename) 45 const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, imageName)
46 46 await processImage(imagePhysicalFile.path, destination, imageSize, true)
47 const imageName = buildUUID() + extension 47
48 const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, imageName) 48 return {
49 await processImage(imagePhysicalFile.path, destination, imageSize) 49 imageName,
50 50 imageSize
51 return retryTransactionWrapper(() => { 51 }
52 return sequelizeTypescript.transaction(async t => { 52 }
53 const actorImageInfo = { 53
54 name: imageName, 54 const processedImages = await Promise.all(ACTOR_IMAGES_SIZE[type].map(processImageSize))
55 fileUrl: null, 55 await remove(imagePhysicalFile.path)
56 height: imageSize.height, 56
57 width: imageSize.width, 57 return retryTransactionWrapper(() => sequelizeTypescript.transaction(async t => {
58 onDisk: true 58 const actorImagesInfo = processedImages.map(({ imageName, imageSize }) => ({
59 } 59 name: imageName,
60 60 fileUrl: null,
61 const updatedActor = await updateActorImageInstance(accountOrChannel.Actor, type, actorImageInfo, t) 61 height: imageSize.height,
62 await updatedActor.save({ transaction: t }) 62 width: imageSize.width,
63 63 onDisk: true
64 await sendUpdateActor(accountOrChannel, t) 64 }))
65 65
66 return type === ActorImageType.AVATAR 66 const updatedActor = await updateActorImages(accountOrChannel.Actor, type, actorImagesInfo, t)
67 ? updatedActor.Avatar 67 await updatedActor.save({ transaction: t })
68 : updatedActor.Banner 68
69 }) 69 await sendUpdateActor(accountOrChannel, t)
70 }) 70
71 return type === ActorImageType.AVATAR
72 ? updatedActor.Avatars
73 : updatedActor.Banners
74 }))
71} 75}
72 76
73async function deleteLocalActorImageFile (accountOrChannel: MAccountDefault | MChannelDefault, type: ActorImageType) { 77async function deleteLocalActorImageFile (accountOrChannel: MAccountDefault | MChannelDefault, type: ActorImageType) {
74 return retryTransactionWrapper(() => { 78 return retryTransactionWrapper(() => {
75 return sequelizeTypescript.transaction(async t => { 79 return sequelizeTypescript.transaction(async t => {
76 const updatedActor = await deleteActorImageInstance(accountOrChannel.Actor, type, t) 80 const updatedActor = await deleteActorImages(accountOrChannel.Actor, type, t)
77 await updatedActor.save({ transaction: t }) 81 await updatedActor.save({ transaction: t })
78 82
79 await sendUpdateActor(accountOrChannel, t) 83 await sendUpdateActor(accountOrChannel, t)
80 84
81 return updatedActor.Avatar 85 return updatedActor.Avatars
82 }) 86 })
83 }) 87 })
84} 88}
85 89
86type DownloadImageQueueTask = { fileUrl: string, filename: string, type: ActorImageType } 90type DownloadImageQueueTask = {
91 fileUrl: string
92 filename: string
93 type: ActorImageType
94 size: typeof ACTOR_IMAGES_SIZE[ActorImageType][0]
95}
87 96
88const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => { 97const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => {
89 const size = task.type === ActorImageType.AVATAR 98 downloadImage(task.fileUrl, CONFIG.STORAGE.ACTOR_IMAGES, task.filename, task.size)
90 ? ACTOR_IMAGES_SIZE.AVATARS
91 : ACTOR_IMAGES_SIZE.BANNERS
92
93 downloadImage(task.fileUrl, CONFIG.STORAGE.ACTOR_IMAGES, task.filename, size)
94 .then(() => cb()) 99 .then(() => cb())
95 .catch(err => cb(err)) 100 .catch(err => cb(err))
96}, QUEUE_CONCURRENCY.ACTOR_PROCESS_IMAGE) 101}, QUEUE_CONCURRENCY.ACTOR_PROCESS_IMAGE)
@@ -110,7 +115,7 @@ const actorImagePathUnsafeCache = new LRUCache<string, string>({ max: LRU_CACHE.
110 115
111export { 116export {
112 actorImagePathUnsafeCache, 117 actorImagePathUnsafeCache,
113 updateLocalActorImageFile, 118 updateLocalActorImageFiles,
114 deleteLocalActorImageFile, 119 deleteLocalActorImageFile,
115 pushActorImageProcessInQueue, 120 pushActorImageProcessInQueue,
116 buildActorInstance 121 buildActorInstance
diff --git a/server/lib/notifier/shared/comment/comment-mention.ts b/server/lib/notifier/shared/comment/comment-mention.ts
index 765cbaad9..ecd1687b4 100644
--- a/server/lib/notifier/shared/comment/comment-mention.ts
+++ b/server/lib/notifier/shared/comment/comment-mention.ts
@@ -77,7 +77,7 @@ export class CommentMention extends AbstractNotification <MCommentOwnerVideo, MU
77 userId: user.id, 77 userId: user.id,
78 commentId: this.payload.id 78 commentId: this.payload.id
79 }) 79 })
80 notification.Comment = this.payload 80 notification.VideoComment = this.payload
81 81
82 return notification 82 return notification
83 } 83 }
diff --git a/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts b/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts
index b76fc15bf..757502703 100644
--- a/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts
+++ b/server/lib/notifier/shared/comment/new-comment-for-video-owner.ts
@@ -44,7 +44,7 @@ export class NewCommentForVideoOwner extends AbstractNotification <MCommentOwner
44 userId: user.id, 44 userId: user.id,
45 commentId: this.payload.id 45 commentId: this.payload.id
46 }) 46 })
47 notification.Comment = this.payload 47 notification.VideoComment = this.payload
48 48
49 return notification 49 return notification
50 } 50 }