(inputBuffer, isAnimated, cropSquare)
| 7 | import webp from 'node-webpmux'; |
| 8 | import crypto from 'crypto'; |
| 9 | async function convertBufferToStickerWebp(inputBuffer, isAnimated, cropSquare) { |
| 10 | const tmpDir = path.join(process.cwd(), 'temp'); |
| 11 | if (!fs.existsSync(tmpDir)) |
| 12 | fs.mkdirSync(tmpDir, { recursive: true }); |
| 13 | const tempInputBase = path.join(tmpDir, `igs_${Date.now()}_${Math.random().toString(36).slice(2)}`); |
| 14 | const tempInput = isAnimated ? `${tempInputBase}.mp4` : `${tempInputBase}.jpg`; |
| 15 | const tempOutput = path.join(tmpDir, `igs_out_${Date.now()}_${Math.random().toString(36).slice(2)}.webp`); |
| 16 | fs.writeFileSync(tempInput, inputBuffer); |
| 17 | const filesToDelete = []; |
| 18 | const scheduleDelete = (p) => { |
| 19 | if (!p) |
| 20 | return; |
| 21 | filesToDelete.push(p); |
| 22 | setTimeout(() => { |
| 23 | try { |
| 24 | fs.unlinkSync(p); |
| 25 | } |
| 26 | catch { } |
| 27 | }, 5000); |
| 28 | }; |
| 29 | const vfCropSquareImg = "crop=min(iw\\,ih):min(iw\\,ih),scale=512:512"; |
| 30 | const vfPadSquareImg = "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000"; |
| 31 | let ffmpegCommand; |
| 32 | if (isAnimated) { |
| 33 | const isLargeVideo = inputBuffer.length > (5 * 1024 * 1024); |
| 34 | if (cropSquare) { |
| 35 | if (isLargeVideo) { |
| 36 | ffmpegCommand = `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}"`; |
| 37 | } |
| 38 | else { |
| 39 | ffmpegCommand = `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}"`; |
| 40 | } |
| 41 | } |
| 42 | else { |
| 43 | if (isLargeVideo) { |
| 44 | ffmpegCommand = `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}"`; |
| 45 | } |
| 46 | else { |
| 47 | ffmpegCommand = `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}"`; |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | else { |
| 52 | const vf = `${cropSquare ? vfCropSquareImg : vfPadSquareImg},format=rgba`; |
| 53 | ffmpegCommand = `ffmpeg -y -i "${tempInput}" -vf "${vf}" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 20 -compression_level 6 "${tempOutput}"`; |
| 54 | } |
| 55 | await new Promise((resolve, reject) => { |
| 56 | exec(ffmpegCommand, (error) => { |
| 57 | if (error) |
| 58 | return reject(error); |
| 59 | resolve(undefined); |
| 60 | }); |
| 61 | }); |
| 62 | let webpBuffer = fs.readFileSync(tempOutput); |
| 63 | scheduleDelete(tempOutput); |
| 64 | if (isAnimated && webpBuffer.length > 1000 * 1024) { |
| 65 | try { |
| 66 | const tempOutput2 = path.join(tmpDir, `igs_out2_${Date.now()}_${Math.random().toString(36).slice(2)}.webp`); |
no test coverage detected