(inputBuffer, isAnimated, cropSquare)
| 8 | import crypto from 'crypto'; |
| 9 | import { stickercropFromBuffer } from './stickercrop.js'; |
| 10 | async function _convertBufferToStickerWebp(inputBuffer, isAnimated, cropSquare) { |
| 11 | const tmpDir = path.join(process.cwd(), 'temp'); |
| 12 | if (!fs.existsSync(tmpDir)) |
| 13 | fs.mkdirSync(tmpDir, { recursive: true }); |
| 14 | const tempInputBase = path.join(tmpDir, `igs_${Date.now()}_${Math.random().toString(36).slice(2)}`); |
| 15 | const tempInput = isAnimated ? `${tempInputBase}.mp4` : `${tempInputBase}.jpg`; |
| 16 | const tempOutput = path.join(tmpDir, `igs_out_${Date.now()}_${Math.random().toString(36).slice(2)}.webp`); |
| 17 | fs.writeFileSync(tempInput, inputBuffer); |
| 18 | const scheduleDelete = (p) => { |
| 19 | if (!p) |
| 20 | return; |
| 21 | setTimeout(() => { |
| 22 | try { |
| 23 | fs.unlinkSync(p); |
| 24 | } |
| 25 | catch { } |
| 26 | }, 5000); |
| 27 | }; |
| 28 | const vfCropSquareImg = "crop=min(iw\\,ih):min(iw\\,ih),scale=512:512"; |
| 29 | const vfPadSquareImg = "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000"; |
| 30 | let ffmpegCommand; |
| 31 | if (isAnimated) { |
| 32 | const isLargeVideo = inputBuffer.length > (5 * 1024 * 1024); |
| 33 | if (cropSquare) { |
| 34 | ffmpegCommand = isLargeVideo |
| 35 | ? `ffmpeg -y -i "${tempInput}" -t 2 -vf "crop=min(iw\\,ih):min(iw\\,ih),scale=512:512,fps=8" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 30 -compression_level 6 -b:v 100k -max_muxing_queue_size 1024 "${tempOutput}"` |
| 36 | : `ffmpeg -y -i "${tempInput}" -t 3 -vf "crop=min(iw\\,ih):min(iw\\,ih),scale=512:512,fps=12" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 50 -compression_level 6 -b:v 150k -max_muxing_queue_size 1024 "${tempOutput}"`; |
| 37 | } |
| 38 | else { |
| 39 | ffmpegCommand = isLargeVideo |
| 40 | ? `ffmpeg -y -i "${tempInput}" -t 2 -vf "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,fps=8" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 35 -compression_level 6 -b:v 100k -max_muxing_queue_size 1024 "${tempOutput}"` |
| 41 | : `ffmpeg -y -i "${tempInput}" -t 3 -vf "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,fps=12" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 45 -compression_level 6 -b:v 150k -max_muxing_queue_size 1024 "${tempOutput}"`; |
| 42 | } |
| 43 | } |
| 44 | else { |
| 45 | const vf = `${cropSquare ? vfCropSquareImg : vfPadSquareImg},format=rgba`; |
| 46 | ffmpegCommand = `ffmpeg -y -i "${tempInput}" -vf "${vf}" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 40 -compression_level 6 "${tempOutput}"`; |
| 47 | } |
| 48 | await new Promise((resolve, reject) => { |
| 49 | exec(ffmpegCommand, (error) => error ? reject(error) : resolve(undefined)); |
| 50 | }); |
| 51 | const webpBuffer = fs.readFileSync(tempOutput); |
| 52 | scheduleDelete(tempOutput); |
| 53 | scheduleDelete(tempInput); |
| 54 | const img = new webp.Image(); |
| 55 | await img.load(webpBuffer); |
| 56 | const json = { |
| 57 | 'sticker-pack-id': crypto.randomBytes(32).toString('hex'), |
| 58 | 'sticker-pack-name': config.packname || 'MegaBot', |
| 59 | 'emojis': ['📸'] |
| 60 | }; |
| 61 | 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]); |
| 62 | const jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8'); |
| 63 | const exif = Buffer.concat([exifAttr, jsonBuffer]); |
| 64 | exif.writeUIntLE(jsonBuffer.length, 14, 4); |
| 65 | img.exif = exif; |
| 66 | return await img.save(null); |
| 67 | } |
nothing calls this directly
no test coverage detected