diff --git a/package-lock.json b/package-lock.json
index 5dd17e968..06555be01 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -936,6 +936,12 @@
"@types/node": "*"
}
},
+ "@types/tmp": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.0.tgz",
+ "integrity": "sha512-flgpHJjntpBAdJD43ShRosQvNC0ME97DCfGvZEDlAThQmnerRXrLbX6YgzRBQCZTthET9eAWFAMaYP0m0Y4HzQ==",
+ "dev": true
+ },
"@types/ws": {
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.2.tgz",
@@ -4039,7 +4045,6 @@
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
- "dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -4050,7 +4055,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
"requires": {
"isexe": "^2.0.0"
}
@@ -4883,30 +4887,25 @@
"dev": true
},
"execa": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
- "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
- "dev": true,
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz",
+ "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==",
"requires": {
- "cross-spawn": "^7.0.0",
- "get-stream": "^5.0.0",
- "human-signals": "^1.1.1",
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
"is-stream": "^2.0.0",
"merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.0",
- "onetime": "^5.1.0",
- "signal-exit": "^3.0.2",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
"strip-final-newline": "^2.0.0"
},
"dependencies": {
"get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "dev": true,
- "requires": {
- "pump": "^3.0.0"
- }
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
}
}
},
@@ -5021,6 +5020,17 @@
"chardet": "^0.7.0",
"iconv-lite": "^0.4.24",
"tmp": "^0.0.33"
+ },
+ "dependencies": {
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ }
}
},
"extglob": {
@@ -6708,10 +6718,9 @@
}
},
"human-signals": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
- "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
- "dev": true
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
+ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
},
"husky": {
"version": "6.0.0",
@@ -7294,8 +7303,7 @@
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
- "dev": true
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
},
"isobject": {
"version": "3.0.1",
@@ -7744,6 +7752,11 @@
"integrity": "sha1-eB4YMpaqlPbU2RbcM10NF676I/g=",
"dev": true
},
+ "lookpath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/lookpath/-/lookpath-1.2.0.tgz",
+ "integrity": "sha512-cUl+R2bGJcSJiHLVKzGHRTYTBhudbHIgd7s63gfGHteaz0BBKEEz2yw2rgbxZAFze92KlbkiWzL1ylYOmqIPVA=="
+ },
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
@@ -8132,8 +8145,7 @@
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
- "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
- "dev": true
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
},
"merge2": {
"version": "1.4.1",
@@ -8167,8 +8179,7 @@
"mimic-fn": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
},
"mimic-response": {
"version": "1.0.1",
@@ -8714,7 +8725,6 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
"integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
"requires": {
"path-key": "^3.0.0"
}
@@ -8889,7 +8899,6 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
"requires": {
"mimic-fn": "^2.1.0"
}
@@ -9207,8 +9216,7 @@
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-parse": {
"version": "1.0.6",
@@ -9392,6 +9400,23 @@
"supports-color": "^7.1.0"
}
},
+ "execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@@ -9402,6 +9427,21 @@
"path-exists": "^4.0.0"
}
},
+ "get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true
+ },
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -10694,7 +10734,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
@@ -10702,8 +10741,7 @@
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
},
"shelljs": {
"version": "0.8.4",
@@ -11233,8 +11271,7 @@
"strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true
+ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
},
"strip-indent": {
"version": "3.0.0",
@@ -11496,12 +11533,11 @@
"dev": true
},
"tmp": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
- "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
- "dev": true,
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
"requires": {
- "os-tmpdir": "~1.0.2"
+ "rimraf": "^3.0.0"
}
},
"to-absolute-glob": {
@@ -12380,6 +12416,40 @@
"dev": true,
"requires": {
"execa": "^4.0.2"
+ },
+ "dependencies": {
+ "execa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz",
+ "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ }
+ },
+ "get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "human-signals": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
+ "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==",
+ "dev": true
+ }
}
},
"winston": {
diff --git a/package.json b/package.json
index b13957d3a..71251ad06 100644
--- a/package.json
+++ b/package.json
@@ -75,6 +75,7 @@
"@types/rimraf": "^3.0.0",
"@types/sharp": "^0.28.0",
"@types/shelljs": "^0.8.8",
+ "@types/tmp": "^0.2.0",
"@types/ws": "^7.4.0",
"@typescript-eslint/eslint-plugin": "^4.16.1",
"@typescript-eslint/parser": "^4.16.1",
@@ -110,9 +111,11 @@
"catch-exit": "^1.1.0",
"chalk": "^4.0.0",
"chrome-launcher": "^0.13.1",
+ "execa": "^5.0.0",
"fsevents": "^2.3.1",
"futoin-hkdf": "^1.3.2",
"latest-version": "^5.1.0",
+ "lookpath": "^1.2.0",
"mime-types": "^2.1.28",
"puppeteer": "^5.5.0",
"puppeteer-extra": "^3.1.16",
@@ -122,6 +125,7 @@
"rimraf": "^3.0.2",
"sanitize-filename": "^1.6.3",
"sharp": "^0.28.0",
+ "tmp": "^0.2.1",
"tree-kill": "^1.2.2",
"winston": "^3.3.3"
},
diff --git a/src/api/layers/sender.layer.ts b/src/api/layers/sender.layer.ts
index 36d8d0824..2965fabef 100644
--- a/src/api/layers/sender.layer.ts
+++ b/src/api/layers/sender.layer.ts
@@ -18,6 +18,7 @@
import * as path from 'path';
import { Page } from 'puppeteer';
import { CreateConfig } from '../../config/create-config';
+import { convertToMP4GIF } from '../../utils/ffmpeg';
import {
base64MimeType,
downloadFileToBase64,
@@ -472,16 +473,74 @@ export class SenderLayer extends ListenerLayer {
to: string,
base64: string,
filename: string,
- caption: string
+ caption?: string
) {
return await this.page.evaluate(
- ({ to, base64, filename, caption }) => {
- WAPI.sendVideoAsGif(base64, to, filename, caption);
- },
+ ({ to, base64, filename, caption }) =>
+ WAPI.sendVideoAsGif(base64, to, filename, caption),
{ to, base64, filename, caption }
);
}
+ /**
+ * Sends a video to given chat as a gif, with caption or not, using base64
+ * @category Chat
+ * @param to Chat id
+ * @param filePath File path
+ * @param filename
+ * @param caption
+ */
+ public async sendGif(
+ to: string,
+ filePath: string,
+ filename?: string,
+ caption?: string
+ ) {
+ return new Promise(async (resolve, reject) => {
+ let base64 = await downloadFileToBase64(filePath),
+ obj: { erro: boolean; to: string; text: string };
+
+ if (!base64) {
+ base64 = await fileToBase64(filePath);
+ }
+
+ if (!base64) {
+ obj = {
+ erro: true,
+ to: to,
+ text: 'No such file or directory, open "' + filePath + '"',
+ };
+ return reject(obj);
+ }
+
+ if (!filename) {
+ filename = path.basename(filePath);
+ }
+
+ this.sendGifFromBase64(to, base64, filename, caption)
+ .then(resolve)
+ .catch(reject);
+ });
+ }
+
+ /**
+ * Sends a video to given chat as a gif, with caption or not, using base64
+ * @category Chat
+ * @param to chat id xxxxx@us.c
+ * @param base64 base64 data:video/xxx;base64,xxx
+ * @param filename string xxxxx
+ * @param caption string xxxxx
+ */
+ public async sendGifFromBase64(
+ to: string,
+ base64: string,
+ filename: string,
+ caption?: string
+ ) {
+ base64 = await convertToMP4GIF(base64);
+
+ return await this.sendVideoAsGifFromBase64(to, base64, filename, caption);
+ }
/**
* Sends contact card to iven chat id
* @category Chat
diff --git a/src/lib/wapi/functions/send-video-as-gif.js b/src/lib/wapi/functions/send-video-as-gif.js
index f4b2e3a58..1cb58fbb2 100644
--- a/src/lib/wapi/functions/send-video-as-gif.js
+++ b/src/lib/wapi/functions/send-video-as-gif.js
@@ -26,17 +26,37 @@ import { base64ToFile } from '../helper';
* @param {string} caption
* @param {Function} done Optional callback
*/
-export function sendVideoAsGif(dataBase64, chatid, filename, caption, done) {
- // const idUser = new window.Store.UserConstructor(chatid);
- const idUser = new Store.WidFactory.createWid(chatid);
- return Store.Chat.find(idUser).then((chat) => {
+export async function sendVideoAsGif(
+ dataBase64,
+ chatid,
+ filename,
+ caption,
+ done
+) {
+ var chat = await WAPI.sendExist(chatid);
+ if (!chat.erro) {
var mediaBlob = base64ToFile(dataBase64, filename);
- processFiles(chat, mediaBlob).then((mc) => {
- var media = mc.models[0];
- media.mediaPrep._mediaData.isGif = true;
- media.mediaPrep._mediaData.gifAttribution = 1;
- media.mediaPrep.sendToChat(chat, { caption: caption });
+ var mediaCollection = await processFiles(chat, mediaBlob);
+ var media = mediaCollection.models[0];
+ media.mediaPrep._mediaData.isGif = true;
+ media.mediaPrep._mediaData.gifAttribution = 1;
+ media.mediaPrep.sendToChat(chat, { caption: caption });
+
+ var result = (await media.sendToChat(chat, { caption: caption })) || '';
+ var m = { filename: filename, text: caption },
+ To = await WAPI.getchatId(chat.id);
+ if (result === 'success' || result === 'OK') {
+ if (done !== undefined) done(false);
+ var obj = WAPI.scope(To, false, result, null);
+ Object.assign(obj, m);
+ return obj;
+ } else {
if (done !== undefined) done(true);
- });
- });
+ var obj = WAPI.scope(To, true, result, null);
+ Object.assign(obj, m);
+ return obj;
+ }
+ } else {
+ return chat;
+ }
}
diff --git a/src/utils/ffmpeg.ts b/src/utils/ffmpeg.ts
new file mode 100644
index 000000000..9b1d0a02f
--- /dev/null
+++ b/src/utils/ffmpeg.ts
@@ -0,0 +1,124 @@
+/*
+ * This file is part of WPPConnect.
+ *
+ * WPPConnect is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WPPConnect is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with WPPConnect. If not, see .
+ */
+import { addExitCallback } from 'catch-exit';
+import execa from 'execa';
+import * as fs from 'fs';
+import * as path from 'path';
+import rimraf from 'rimraf';
+import * as tmp from 'tmp';
+import { lookpath } from 'lookpath';
+
+const tmpDir = tmp.dirSync({});
+
+let i = 0;
+
+addExitCallback((signal) => {
+ // Remove only on exit signal
+ try {
+ // Use rimraf because it is synchronous
+ rimraf.sync(tmpDir.name);
+ } catch (error) {}
+});
+
+export async function getFfmpegPath() {
+ let ffmpegPath = process.env['FFMPEG_PATH'];
+
+ if (ffmpegPath) {
+ const isExecutable = await fs.promises
+ .access(ffmpegPath, fs.constants.X_OK)
+ .then(() => true)
+ .catch(() => false);
+
+ if (isExecutable) {
+ return ffmpegPath;
+ }
+ }
+
+ ffmpegPath = await lookpath('ffmpeg', {
+ include: [
+ 'C:\\FFmpeg\\bin',
+ 'C:\\FFmpeg\\FFmpeg\\bin',
+ 'C:\\Program Files\\ffmpeg\\bin',
+ 'C:\\Program Files (x86)\\ffmpeg\\bin',
+ ],
+ });
+
+ if (!ffmpegPath) {
+ try {
+ ffmpegPath = require('ffmpeg-static');
+ } catch (error) {}
+ }
+
+ if (!ffmpegPath) {
+ throw new Error(
+ 'Error: FFMPEG not found. Please install ffmpeg or define the env FFMPEG_PATH or install ffmpeg-static'
+ );
+ }
+
+ return ffmpegPath;
+}
+
+/**
+ * Convert a file to a compatible MP4-GIF for WhatsApp
+ * @param inputBase64 Gif in base64 format
+ * @returns base64 of a MP4-GIF for WhatsApp
+ */
+export async function convertToMP4GIF(inputBase64: string): Promise {
+ const inputPath = path.join(tmpDir.name, `${i++}`);
+ const outputPath = path.join(tmpDir.name, `${i++}.mp4`);
+
+ if (inputBase64.includes(',')) {
+ inputBase64 = inputBase64.split(',')[1];
+ }
+
+ fs.writeFileSync(inputPath, Buffer.from(inputBase64, 'base64'));
+
+ /**
+ * fluent-ffmpeg is a good alternative,
+ * but to work with MP4 you must use fisical file or ffmpeg will not work
+ * Because of that, I made the choice to use temporary file
+ */
+ const ffmpegPath = await getFfmpegPath();
+
+ try {
+ const out = await execa(ffmpegPath, [
+ '-i',
+ inputPath,
+ '-movflags',
+ 'faststart',
+ '-pix_fmt',
+ 'yuv420p',
+ '-vf',
+ 'scale=trunc(iw/2)*2:trunc(ih/2)*2',
+ '-f',
+ 'mp4',
+ outputPath,
+ ]);
+
+ if (out.exitCode === 0) {
+ const outputBase64 = fs.readFileSync(outputPath);
+ return 'data:video/mp4;base64,' + outputBase64.toString('base64');
+ }
+
+ throw out.stderr;
+ } finally {
+ rimraf(inputPath, () => null);
+ rimraf(outputPath, () => null);
+ }
+
+ return '';
+}