+
diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts b/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
index 5daa54cb5..de78a390e 100644
--- a/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
+++ b/client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
@@ -17,16 +17,12 @@ export class ActorAvatarInfoComponent implements OnInit {
@Output() avatarChange = new EventEmitter()
- maxSizeText: string
-
private serverConfig: ServerConfig
constructor (
private serverService: ServerService,
private notifier: Notifier
- ) {
- this.maxSizeText = $localize`max size`
- }
+ ) { }
ngOnInit (): void {
this.serverConfig = this.serverService.getTmpConfig()
@@ -58,4 +54,8 @@ export class ActorAvatarInfoComponent implements OnInit {
get avatarExtensions () {
return this.serverConfig.avatar.file.extensions.join(', ')
}
+
+ get avatarFormat () {
+ return `${$localize`max size`}: 192*192px, ${this.maxAvatarSizeInBytes} ${$localize`extensions`}: ${this.avatarExtensions}`
+ }
}
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts
index c8d6969ff..66b9d2e44 100644
--- a/server/helpers/ffmpeg-utils.ts
+++ b/server/helpers/ffmpeg-utils.ts
@@ -355,6 +355,40 @@ function convertWebPToJPG (path: string, destination: string): Promise {
})
}
+function processGIF (
+ path: string,
+ destination: string,
+ newSize: { width: number, height: number },
+ keepOriginal = false
+): Promise {
+ return new Promise(async (res, rej) => {
+ if (path === destination) {
+ throw new Error('FFmpeg needs an input path different that the output path.')
+ }
+
+ logger.debug('Processing gif %s to %s.', path, destination)
+
+ try {
+ const command = ffmpeg(path)
+ .fps(20)
+ .size(`${newSize.width}x${newSize.height}`)
+ .output(destination)
+
+ command.on('error', (err, stdout, stderr) => {
+ logger.error('Error in ffmpeg gif resizing process.', { stdout, stderr })
+ return rej(err)
+ })
+ .on('end', async () => {
+ if (keepOriginal !== true) await remove(path)
+ res()
+ })
+ .run()
+ } catch (err) {
+ return rej(err)
+ }
+ })
+}
+
function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: number[], fps, deleteSegments: boolean) {
const command = getFFmpeg(rtmpUrl)
command.inputOption('-fflags nobuffer')
@@ -474,6 +508,7 @@ export {
getAudioStreamCodec,
runLiveMuxing,
convertWebPToJPG,
+ processGIF,
getVideoStreamSize,
getVideoFileResolution,
getMetadataFromFile,
diff --git a/server/helpers/image-utils.ts b/server/helpers/image-utils.ts
index 5f254a7aa..fdf06e848 100644
--- a/server/helpers/image-utils.ts
+++ b/server/helpers/image-utils.ts
@@ -1,5 +1,6 @@
+import { extname } from 'path'
import { remove, rename } from 'fs-extra'
-import { convertWebPToJPG } from './ffmpeg-utils'
+import { convertWebPToJPG, processGIF } from './ffmpeg-utils'
import { logger } from './logger'
const Jimp = require('jimp')
@@ -10,6 +11,13 @@ async function processImage (
newSize: { width: number, height: number },
keepOriginal = false
) {
+ const extension = extname(path)
+
+ // Use FFmpeg to process GIF
+ if (extension === '.gif') {
+ return processGIF(path, destination, newSize, keepOriginal)
+ }
+
if (path === destination) {
throw new Error('Jimp needs an input path different that the output path.')
}
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 79e6a744c..5c6d06077 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -291,7 +291,7 @@ const CONSTRAINTS_FIELDS = {
PRIVATE_KEY: { min: 10, max: 5000 }, // Length
URL: { min: 3, max: 2000 }, // Length
AVATAR: {
- EXTNAME: [ '.png', '.jpeg', '.jpg' ],
+ EXTNAME: [ '.png', '.jpeg', '.jpg', '.gif' ],
FILE_SIZE: {
max: 2 * 1024 * 1024 // 2MB
}