Browse Source

drastically improve GIF quality by using HQ source

Mike L 4 years ago
parent
commit
117c1adfa4
4 changed files with 26 additions and 16 deletions
  1. 8 5
      dist/gifski.js
  2. 5 3
      dist/webshot.js
  3. 8 5
      src/gifski.ts
  4. 5 3
      src/webshot.ts

+ 8 - 5
dist/gifski.js

@@ -5,7 +5,7 @@ const fs_1 = require("fs");
 const temp = require("temp");
 const loggers_1 = require("./loggers");
 const logger = loggers_1.getLogger('gifski');
-function default_1(data) {
+function default_1(data, targetWidth) {
     const outputFilePath = temp.path({ suffix: '.gif' });
     // temp.track();
     try {
@@ -14,15 +14,18 @@ function default_1(data) {
         fs_1.closeSync(inputFile.fd);
         logger.info(`saved video file to ${inputFile.path}, starting gif conversion...`);
         const args = [
+            inputFile.path,
+            '-o',
+            outputFilePath,
             '--fps',
             '12.5',
             '--quiet',
             '--quality',
-            '80',
-            '-o',
-            outputFilePath,
-            inputFile.path,
+            '90',
         ];
+        if (typeof (targetWidth) === 'number') {
+            args.push('--width', (Math.ceil(targetWidth / 2) * 2).toString());
+        }
         logger.info(` gifski ${args.join(' ')}`);
         const gifskiInvocation = child_process_1.spawnSync('gifski', args, { encoding: 'utf8', timeout: 90000 });
         if (gifskiInvocation.stderr)

+ 5 - 3
dist/webshot.js

@@ -188,7 +188,9 @@ class Webshot extends CallableInstance {
                     case 'png':
                         return 'image/png';
                     case 'mp4':
-                        data = gifski_1.default(data);
+                        const dims = url.match(/\/(\d+)x(\d+)\//).slice(1).map(Number);
+                        const factor = dims.some(x => x >= 960) ? 0.375 : 0.5;
+                        data = gifski_1.default(data, dims[0] * factor);
                         return 'image/gif';
                 }
             })(url.split('/').slice(-1)[0].match(/\.([^:?&]+)/)[1]);
@@ -256,8 +258,8 @@ class Webshot extends CallableInstance {
                         else {
                             url = media.video_info.variants
                                 .filter(variant => variant.bitrate)
-                                .sort((var1, var2) => var1.bitrate - var2.bitrate)
-                                .map(variant => variant.url)[0]; // smallest video
+                                .sort((var1, var2) => var2.bitrate - var1.bitrate)
+                                .map(variant => variant.url)[0]; // largest video
                         }
                         const altMessage = mirai_1.Message.Plain(`[失败的${typeInZH[media.type].type}:${url}]`);
                         promise = promise.then(() => this.fetchMedia(url))

+ 8 - 5
src/gifski.ts

@@ -6,7 +6,7 @@ import { getLogger } from './loggers';
 
 const logger = getLogger('gifski');
 
-export default function (data: ArrayBuffer) {
+export default function (data: ArrayBuffer, targetWidth?: number) {
     const outputFilePath = temp.path({suffix: '.gif'});
     // temp.track();
     try {
@@ -15,15 +15,18 @@ export default function (data: ArrayBuffer) {
       closeSync(inputFile.fd);
       logger.info(`saved video file to ${inputFile.path}, starting gif conversion...`);
       const args = [
+        inputFile.path,
+        '-o',
+        outputFilePath,
         '--fps',
         '12.5',
         '--quiet',
         '--quality',
-        '80',
-        '-o',
-        outputFilePath,
-        inputFile.path,
+        '90',
       ];
+      if (typeof(targetWidth) === 'number') {
+        args.push('--width', (Math.ceil(targetWidth / 2) * 2).toString());
+      }
       logger.info(` gifski ${args.join(' ')}`);
       const gifskiInvocation = spawnSync('gifski', args, {encoding: 'utf8', timeout: 90000});
       if (gifskiInvocation.stderr) throw Error(gifskiInvocation.stderr);

+ 5 - 3
src/webshot.ts

@@ -210,7 +210,9 @@ extends CallableInstance<
           case 'png':
             return 'image/png';
           case 'mp4':
-            data = gifski(data);
+            const dims: number[] = url.match(/\/(\d+)x(\d+)\//).slice(1).map(Number);
+            const factor = dims.some(x => x >= 960) ? 0.375 : 0.5;
+            data = gifski(data, dims[0] * factor);
             return 'image/gif';
         }
       })(url.split('/').slice(-1)[0].match(/\.([^:?&]+)/)[1]);
@@ -279,8 +281,8 @@ extends CallableInstance<
             } else {
               url = media.video_info.variants
                 .filter(variant => variant.bitrate)
-                .sort((var1, var2) => var1.bitrate - var2.bitrate)
-                .map(variant => variant.url)[0]; // smallest video
+                .sort((var1, var2) => var2.bitrate - var1.bitrate)
+                .map(variant => variant.url)[0]; // largest video
             }
             const altMessage = Message.Plain(`[失败的${typeInZH[media.type].type}:${url}]`);
             promise = promise.then(() => this.fetchMedia(url))