| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- import { dirname } from 'path'
- import { fileURLToPath } from 'url'
- import * as fs from 'fs'
- import * as path from 'path'
- import * as crypto from 'crypto'
- import { ffmpeg } from './converter.js'
- import fluent_ffmpeg from 'fluent-ffmpeg'
- import { spawn } from 'child_process'
- import uploadFile from './uploadFile.js'
- import uploadImage from './uploadImage.js'
- import { fileTypeFromBuffer } from 'file-type'
- import webp from 'node-webpmux'
- import fetch from 'node-fetch'
- const __dirname = dirname(fileURLToPath(import.meta.url))
- const tmp = path.join(__dirname, '../tmp')
- /**
- * Image to Sticker
- * @param {Buffer} img Image Buffer
- * @param {String} url Image URL
- */
- function sticker2(img, url) {
- return new Promise(async (resolve, reject) => {
- try {
- if (url) {
- const res = await fetch(url)
- if (res.status !== 200) throw await res.text()
- img = await res.buffer()
- }
- const inp = path.join(tmp, +new Date + '.jpeg')
- await fs.promises.writeFile(inp, img)
- const ff = spawn('ffmpeg', [
- '-y',
- '-i', inp,
- '-vf', 'scale=512:512:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1',
- '-f', 'png',
- '-'
- ])
- ff.on('error', reject)
- ff.on('close', async () => {
- await fs.promises.unlink(inp)
- })
- const bufs = []
- const [_spawnprocess, ..._spawnargs] = [...(module.exports.support.gm ? ['gm'] : module.exports.magick ? ['magick'] : []), 'convert', 'png:-', 'webp:-']
- const im = spawn(_spawnprocess, _spawnargs)
- im.on('error', e => conn.reply(m.chat, util.format(e), m))
- im.stdout.on('data', chunk => bufs.push(chunk))
- ff.stdout.pipe(im.stdin)
- im.on('exit', () => {
- resolve(Buffer.concat(bufs))
- })
- } catch (e) {
- reject(e)
- }
- })
- }
- /**
- * Image/Video to Sticker
- * @param {Buffer} img Image/Video Buffer
- * @param {String} url Image/Video URL
- * @param {String} packname EXIF Packname
- * @param {String} author EXIF Author
- */
- async function sticker3(img, url, packname, author) {
- url = url ? url : await uploadFile(img)
- const res = await fetch('https://api.xteam.xyz/sticker/wm?' + new URLSearchParams(Object.entries({
- url,
- packname,
- author
- })))
- return await res.buffer()
- }
- /**
- * Image to Sticker
- * @param {Buffer} img Image/Video Buffer
- * @param {String} url Image/Video URL
- */
- async function sticker4(img, url) {
- if (url) {
- const res = await fetch(url)
- if (res.status !== 200) throw await res.text()
- img = await res.buffer()
- }
- return await ffmpeg(img, [
- '-vf', 'scale=512:512:flags=lanczos:force_original_aspect_ratio=decrease,format=rgba,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1'
- ], 'jpeg', 'webp')
- }
- async function sticker5(img, url, packname, author, categories = [''], extra = {}) {
- const { Sticker } = await import('wa-sticker-formatter')
- const stickerMetadata = {
- type: 'default',
- pack: packname,
- author,
- categories,
- ...extra
- }
- return (new Sticker(img ? img : url, stickerMetadata)).toBuffer()
- }
- /**
- * Convert using fluent-ffmpeg
- * @param {string} img
- * @param {string} url
- */
- function sticker6(img, url) {
- return new Promise(async (resolve, reject) => {
- if (url) {
- const res = await fetch(url)
- if (res.status !== 200) throw await res.text()
- img = await res.buffer()
- }
- const type = await fileTypeFromBuffer(img) || {
- mime: 'application/octet-stream',
- ext: 'bin'
- }
- if (type.ext == 'bin') reject(img)
- const tmp = path.join(__dirname, `../tmp/${+ new Date()}.${type.ext}`)
- const out = path.join(tmp + '.webp')
- await fs.promises.writeFile(tmp, img)
- // https://github.com/MhankBarBar/termux-wabot/blob/main/index.js#L313#L368
- const Fffmpeg = /video/i.test(type.mime) ? fluent_ffmpeg(tmp).inputFormat(type.ext) : fluent_ffmpeg(tmp).input(tmp)
- Fffmpeg
- .on('error', function (err) {
- console.error(err)
- fs.promises.unlink(tmp)
- reject(img)
- })
- .on('end', async function () {
- fs.promises.unlink(tmp)
- resolve(await fs.promises.readFile(out))
- })
- .addOutputOptions([
- `-vcodec`, `libwebp`, `-vf`,
- `scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:color=white@0.0, split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse`
- ])
- .toFormat('webp')
- .save(out)
- })
- }
- /**
- * Add WhatsApp JSON Exif Metadata
- * Taken from https://github.com/pedroslopez/whatsapp-web.js/pull/527/files
- * @param {Buffer} webpSticker
- * @param {String} packname
- * @param {String} author
- * @param {String} categories
- * @param {Object} extra
- * @returns
- */
- async function addExif(webpSticker, packname, author, categories = [''], extra = {}) {
- const img = new webp.Image();
- const stickerPackId = crypto.randomBytes(32).toString('hex');
- const json = { 'sticker-pack-id': stickerPackId, 'sticker-pack-name': packname, 'sticker-pack-publisher': author, 'emojis': categories, ...extra };
- const exifAttr = Buffer.from([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]);
- const jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8');
- const exif = Buffer.concat([exifAttr, jsonBuffer]);
- exif.writeUIntLE(jsonBuffer.length, 14, 4);
- await img.load(webpSticker)
- img.exif = exif
- return await img.save(null)
- }
- /**
- * Image/Video to Sticker
- * @param {Buffer} img Image/Video Buffer
- * @param {String} url Image/Video URL
- * @param {...String}
- */
- async function sticker(img, url, ...args) {
- let lastError, stiker
- for (const func of [
- sticker3, global.support.ffmpeg && sticker6, sticker5,
- global.support.ffmpeg && global.support.ffmpegWebp && sticker4,
- global.support.ffmpeg && (global.support.convert || global.support.magick || global.support.gm) && sticker2,
- ].filter(f => f)) {
- try {
- stiker = await func(img, url, ...args)
- if (stiker.includes('html')) continue
- if (stiker.includes('WEBP')) {
- try {
- return await addExif(stiker, ...args)
- } catch (e) {
- console.error(e)
- return stiker
- }
- }
- throw stiker.toString()
- } catch (err) {
- lastError = err
- continue
- }
- }
- console.error(lastError)
- return lastError
- }
- const support = {
- ffmpeg: true,
- ffprobe: true,
- ffmpegWebp: true,
- convert: true,
- magick: false,
- gm: false,
- find: false
- }
- export {
- sticker,
- sticker2,
- sticker3,
- sticker4,
- sticker6,
- addExif,
- support
- }
|