]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Add pixel size to tooltip and gif support with FFmpeg for avatar upload (#3329)
authorKimsible <1877318+kimsible@users.noreply.github.com>
Wed, 25 Nov 2020 08:26:31 +0000 (09:26 +0100)
committerGitHub <noreply@github.com>
Wed, 25 Nov 2020 08:26:31 +0000 (09:26 +0100)
* 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>
client/src/app/shared/shared-main/account/actor-avatar-info.component.html
client/src/app/shared/shared-main/account/actor-avatar-info.component.ts
server/helpers/ffmpeg-utils.ts
server/helpers/image-utils.ts
server/initializers/constants.ts

index d01b9ac7fdf0ea189f40e756e306b5d88013462a..e63d8de2d5448acf7c869633ed56b193eaa4b780 100644 (file)
@@ -4,7 +4,8 @@
       <img [src]="actor.avatarUrl" alt="Avatar" />
 
       <div class="actor-img-edit-container">
-        <div class="actor-img-edit-button" [ngbTooltip]="'(extensions: '+ avatarExtensions +', '+ maxSizeText +': '+ maxAvatarSizeInBytes +')'" placement="right" container="body">
+        <div class="actor-img-edit-button" [ngbTooltip]="avatarFormat"
+          placement="right" container="body">
           <my-global-icon iconName="edit"></my-global-icon>
           <label for="avatarfile" i18n>Change your avatar</label>
           <input #avatarfileInput type="file" title=" " name="avatarfile" id="avatarfile" [accept]="avatarExtensions" (change)="onAvatarChange()"/>
index 5daa54cb59c208806d156350a3c8a02b3d0ab253..de78a390e1ead62e81e92d2d742e5ff4a8a26cd5 100644 (file)
@@ -17,16 +17,12 @@ export class ActorAvatarInfoComponent implements OnInit {
 
   @Output() avatarChange = new EventEmitter<FormData>()
 
-  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}`
+  }
 }
index c8d6969ffbd3bc478afcbb7a35acd85b84819a66..66b9d2e44804388cf377394b8f4943383202d58b 100644 (file)
@@ -355,6 +355,40 @@ function convertWebPToJPG (path: string, destination: string): Promise<void> {
   })
 }
 
+function processGIF (
+  path: string,
+  destination: string,
+  newSize: { width: number, height: number },
+  keepOriginal = false
+): Promise<void> {
+  return new Promise<void>(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,
index 5f254a7aaf04594de6af26331ef667b8c0b23eef..fdf06e848b86a294fa662d23751c699b519260c1 100644 (file)
@@ -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.')
   }
index 79e6a744c27a0110c7d050e1c4449fc02500bd7e..5c6d060775081b51e8ab217008938aefc5936503 100644 (file)
@@ -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
       }