aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKimsible <1877318+kimsible@users.noreply.github.com>2020-11-25 09:26:31 +0100
committerGitHub <noreply@github.com>2020-11-25 09:26:31 +0100
commit123f61933611f326ea5a5e8c2ea253ee8720e4f0 (patch)
tree49ff0e98eaffc389f33fb57bd1b42735fe78bce8
parentc07fac202dba3fed69aace74157589c21d732be6 (diff)
downloadPeerTube-123f61933611f326ea5a5e8c2ea253ee8720e4f0.tar.gz
PeerTube-123f61933611f326ea5a5e8c2ea253ee8720e4f0.tar.zst
PeerTube-123f61933611f326ea5a5e8c2ea253ee8720e4f0.zip
Add pixel size to tooltip and gif support with FFmpeg for avatar upload (#3329)
* Add avatar pixel size upload in tooltip * Add gif support for avatar * Add ffmpeg GIF process Co-authored-by: kimsible <kimsible@users.noreply.github.com>
-rw-r--r--client/src/app/shared/shared-main/account/actor-avatar-info.component.html3
-rw-r--r--client/src/app/shared/shared-main/account/actor-avatar-info.component.ts10
-rw-r--r--server/helpers/ffmpeg-utils.ts35
-rw-r--r--server/helpers/image-utils.ts10
-rw-r--r--server/initializers/constants.ts2
5 files changed, 52 insertions, 8 deletions
diff --git a/client/src/app/shared/shared-main/account/actor-avatar-info.component.html b/client/src/app/shared/shared-main/account/actor-avatar-info.component.html
index d01b9ac7f..e63d8de2d 100644
--- a/client/src/app/shared/shared-main/account/actor-avatar-info.component.html
+++ b/client/src/app/shared/shared-main/account/actor-avatar-info.component.html
@@ -4,7 +4,8 @@
4 <img [src]="actor.avatarUrl" alt="Avatar" /> 4 <img [src]="actor.avatarUrl" alt="Avatar" />
5 5
6 <div class="actor-img-edit-container"> 6 <div class="actor-img-edit-container">
7 <div class="actor-img-edit-button" [ngbTooltip]="'(extensions: '+ avatarExtensions +', '+ maxSizeText +': '+ maxAvatarSizeInBytes +')'" placement="right" container="body"> 7 <div class="actor-img-edit-button" [ngbTooltip]="avatarFormat"
8 placement="right" container="body">
8 <my-global-icon iconName="edit"></my-global-icon> 9 <my-global-icon iconName="edit"></my-global-icon>
9 <label for="avatarfile" i18n>Change your avatar</label> 10 <label for="avatarfile" i18n>Change your avatar</label>
10 <input #avatarfileInput type="file" title=" " name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange()"/> 11 <input #avatarfileInput type="file" title=" " name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange()"/>
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 {
17 17
18 @Output() avatarChange = new EventEmitter<FormData>() 18 @Output() avatarChange = new EventEmitter<FormData>()
19 19
20 maxSizeText: string
21
22 private serverConfig: ServerConfig 20 private serverConfig: ServerConfig
23 21
24 constructor ( 22 constructor (
25 private serverService: ServerService, 23 private serverService: ServerService,
26 private notifier: Notifier 24 private notifier: Notifier
27 ) { 25 ) { }
28 this.maxSizeText = $localize`max size`
29 }
30 26
31 ngOnInit (): void { 27 ngOnInit (): void {
32 this.serverConfig = this.serverService.getTmpConfig() 28 this.serverConfig = this.serverService.getTmpConfig()
@@ -58,4 +54,8 @@ export class ActorAvatarInfoComponent implements OnInit {
58 get avatarExtensions () { 54 get avatarExtensions () {
59 return this.serverConfig.avatar.file.extensions.join(', ') 55 return this.serverConfig.avatar.file.extensions.join(', ')
60 } 56 }
57
58 get avatarFormat () {
59 return `${$localize`max size`}: 192*192px, ${this.maxAvatarSizeInBytes} ${$localize`extensions`}: ${this.avatarExtensions}`
60 }
61} 61}
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<void> {
355 }) 355 })
356} 356}
357 357
358function processGIF (
359 path: string,
360 destination: string,
361 newSize: { width: number, height: number },
362 keepOriginal = false
363): Promise<void> {
364 return new Promise<void>(async (res, rej) => {
365 if (path === destination) {
366 throw new Error('FFmpeg needs an input path different that the output path.')
367 }
368
369 logger.debug('Processing gif %s to %s.', path, destination)
370
371 try {
372 const command = ffmpeg(path)
373 .fps(20)
374 .size(`${newSize.width}x${newSize.height}`)
375 .output(destination)
376
377 command.on('error', (err, stdout, stderr) => {
378 logger.error('Error in ffmpeg gif resizing process.', { stdout, stderr })
379 return rej(err)
380 })
381 .on('end', async () => {
382 if (keepOriginal !== true) await remove(path)
383 res()
384 })
385 .run()
386 } catch (err) {
387 return rej(err)
388 }
389 })
390}
391
358function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: number[], fps, deleteSegments: boolean) { 392function runLiveTranscoding (rtmpUrl: string, outPath: string, resolutions: number[], fps, deleteSegments: boolean) {
359 const command = getFFmpeg(rtmpUrl) 393 const command = getFFmpeg(rtmpUrl)
360 command.inputOption('-fflags nobuffer') 394 command.inputOption('-fflags nobuffer')
@@ -474,6 +508,7 @@ export {
474 getAudioStreamCodec, 508 getAudioStreamCodec,
475 runLiveMuxing, 509 runLiveMuxing,
476 convertWebPToJPG, 510 convertWebPToJPG,
511 processGIF,
477 getVideoStreamSize, 512 getVideoStreamSize,
478 getVideoFileResolution, 513 getVideoFileResolution,
479 getMetadataFromFile, 514 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 @@
1import { extname } from 'path'
1import { remove, rename } from 'fs-extra' 2import { remove, rename } from 'fs-extra'
2import { convertWebPToJPG } from './ffmpeg-utils' 3import { convertWebPToJPG, processGIF } from './ffmpeg-utils'
3import { logger } from './logger' 4import { logger } from './logger'
4 5
5const Jimp = require('jimp') 6const Jimp = require('jimp')
@@ -10,6 +11,13 @@ async function processImage (
10 newSize: { width: number, height: number }, 11 newSize: { width: number, height: number },
11 keepOriginal = false 12 keepOriginal = false
12) { 13) {
14 const extension = extname(path)
15
16 // Use FFmpeg to process GIF
17 if (extension === '.gif') {
18 return processGIF(path, destination, newSize, keepOriginal)
19 }
20
13 if (path === destination) { 21 if (path === destination) {
14 throw new Error('Jimp needs an input path different that the output path.') 22 throw new Error('Jimp needs an input path different that the output path.')
15 } 23 }
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 = {
291 PRIVATE_KEY: { min: 10, max: 5000 }, // Length 291 PRIVATE_KEY: { min: 10, max: 5000 }, // Length
292 URL: { min: 3, max: 2000 }, // Length 292 URL: { min: 3, max: 2000 }, // Length
293 AVATAR: { 293 AVATAR: {
294 EXTNAME: [ '.png', '.jpeg', '.jpg' ], 294 EXTNAME: [ '.png', '.jpeg', '.jpg', '.gif' ],
295 FILE_SIZE: { 295 FILE_SIZE: {
296 max: 2 * 1024 * 1024 // 2MB 296 max: 2 * 1024 * 1024 // 2MB
297 } 297 }