| 199 | |
| 200 | // Helper: convert a raw media buffer to a cropped sticker using same pipeline |
| 201 | async function stickercropFromBuffer(inputBuffer, isAnimated) { |
| 202 | const tmpDir = path.join(process.cwd(), 'tmp'); |
| 203 | if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true }); |
| 204 | |
| 205 | const tempInput = path.join(tmpDir, `cropbuf_${Date.now()}`); |
| 206 | const tempOutput = path.join(tmpDir, `cropbuf_out_${Date.now()}.webp`); |
| 207 | |
| 208 | fs.writeFileSync(tempInput, inputBuffer); |
| 209 | |
| 210 | // Size-based trim like stickercrop |
| 211 | const fileSizeKB = inputBuffer.length / 1024; |
| 212 | const isLargeFile = fileSizeKB > 5000; |
| 213 | |
| 214 | let ffmpegCommand; |
| 215 | if (isAnimated) { |
| 216 | if (isLargeFile) { |
| 217 | 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}"`; |
| 218 | } else { |
| 219 | 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}"`; |
| 220 | } |
| 221 | } else { |
| 222 | ffmpegCommand = `ffmpeg -y -i "${tempInput}" -vf "crop=min(iw\\,ih):min(iw\\,ih),scale=512:512,format=rgba" -c:v libwebp -preset default -loop 0 -vsync 0 -pix_fmt yuva420p -quality 75 -compression_level 6 "${tempOutput}"`; |
| 223 | } |
| 224 | |
| 225 | await new Promise((resolve, reject) => { |
| 226 | exec(ffmpegCommand, (error) => { |
| 227 | if (error) return reject(error); |
| 228 | resolve(); |
| 229 | }); |
| 230 | }); |
| 231 | |
| 232 | const webpBuffer = fs.readFileSync(tempOutput); |
| 233 | |
| 234 | const img = new webp.Image(); |
| 235 | await img.load(webpBuffer); |
| 236 | const json = { |
| 237 | 'sticker-pack-id': crypto.randomBytes(32).toString('hex'), |
| 238 | 'sticker-pack-name': settings.packname || 'KnightBot', |
| 239 | 'emojis': ['✂️'] |
| 240 | }; |
| 241 | 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]); |
| 242 | const jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8'); |
| 243 | const exif = Buffer.concat([exifAttr, jsonBuffer]); |
| 244 | exif.writeUIntLE(jsonBuffer.length, 14, 4); |
| 245 | img.exif = exif; |
| 246 | const finalBuffer = await img.save(null); |
| 247 | |
| 248 | try { |
| 249 | fs.unlinkSync(tempInput); |
| 250 | fs.unlinkSync(tempOutput); |
| 251 | } catch {} |
| 252 | |
| 253 | return finalBuffer; |
| 254 | } |
| 255 | |
| 256 | module.exports.stickercropFromBuffer = stickercropFromBuffer; |