|
| 1 | +/* |
| 2 | +╭━━━━┳╮╱╱╭┳━━━┳━━━┳━╮╱╭┳━━┳━╮╭━╮ |
| 3 | +╰━━╮━┃╰╮╭╯┃╭━╮┃╭━╮┃┃╰╮┃┣┫┣┻╮╰╯╭╯ |
| 4 | +╱╱╭╯╭┻╮╰╯╭┫╰━╯┃┃╱┃┃╭╮╰╯┃┃┃╱╰╮╭╯ |
| 5 | +╱╭╯╭╯╱╰╮╭╯┃╭╮╭┫┃╱┃┃┃╰╮┃┃┃┃╱╭╯╰╮ |
| 6 | +╭╯━╰━╮╱┃┃╱┃┃┃╰┫╰━╯┃┃╱┃┃┣┫┣┳╯╭╮╰╮ |
| 7 | +╰━━━━╯╱╰╯╱╰╯╰━┻━━━┻╯╱╰━┻━━┻━╯╰━╯ |
| 8 | +
|
| 9 | +* @project_name Zyronix Bot [WA Multi-device] |
| 10 | +* @author MzN KinG <https://github.com/mznking> |
| 11 | +* @description A WhatsApp based 3ʳᵈ party application that provide many services with a real-time automated conversational experience |
| 12 | +* @link <https://github.com/mznking/ZyronixBot> |
| 13 | +* @version 1.0.0 |
| 14 | +* @file exif.js - File of exif library. |
| 15 | +
|
| 16 | +All rights reserved. Copyright © Maazin Ahamed 2023. No part of this Zyronix WhatsApp Multi-Device Bot project may be reproduced, distributed, or transmitted in any form or by any means, including photocopying, recording, or other electronic or mechanical methods, without the prior written permission of the copyright holder, except in the case of brief quotations embodied in critical reviews and certain other noncommercial uses permitted by copyright law. For permission requests, contact [email protected] |
| 17 | +
|
| 18 | +This project is licensed under GPL-3.0 license. |
| 19 | +
|
| 20 | +WhatsApp: https://whatsapp.com/channel/0029VaJFtkeEFeXlBhk0CD3I |
| 21 | +Github: https://github.com/mznking |
| 22 | +YouTube: https://youtube.com/@mznking |
| 23 | +*/ |
| 24 | + |
| 25 | +const fs = require('fs') |
| 26 | +const { tmpdir } = require("os") |
| 27 | +const Crypto = require("crypto") |
| 28 | +const ff = require('fluent-ffmpeg') |
| 29 | +const webp = require("node-webpmux") |
| 30 | +const path = require("path") |
| 31 | + |
| 32 | + |
| 33 | +async function imageToWebp (media) { |
| 34 | + |
| 35 | + const tmpFileOut = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 36 | + const tmpFileIn = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.jpg`) |
| 37 | + |
| 38 | + fs.writeFileSync(tmpFileIn, media) |
| 39 | + |
| 40 | + await new Promise((resolve, reject) => { |
| 41 | + ff(tmpFileIn) |
| 42 | + .on("error", reject) |
| 43 | + .on("end", () => resolve(true)) |
| 44 | + .addOutputOptions([ |
| 45 | + "-vcodec", |
| 46 | + "libwebp", |
| 47 | + "-vf", |
| 48 | + "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:[email protected], split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse" |
| 49 | + ]) |
| 50 | + .toFormat("webp") |
| 51 | + .save(tmpFileOut) |
| 52 | + }) |
| 53 | + |
| 54 | + const buff = fs.readFileSync(tmpFileOut) |
| 55 | + fs.unlinkSync(tmpFileOut) |
| 56 | + fs.unlinkSync(tmpFileIn) |
| 57 | + return buff |
| 58 | +} |
| 59 | + |
| 60 | +async function videoToWebp (media) { |
| 61 | + |
| 62 | + const tmpFileOut = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 63 | + const tmpFileIn = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.mp4`) |
| 64 | + |
| 65 | + fs.writeFileSync(tmpFileIn, media) |
| 66 | + |
| 67 | + await new Promise((resolve, reject) => { |
| 68 | + ff(tmpFileIn) |
| 69 | + .on("error", reject) |
| 70 | + .on("end", () => resolve(true)) |
| 71 | + .addOutputOptions([ |
| 72 | + "-vcodec", |
| 73 | + "libwebp", |
| 74 | + "-vf", |
| 75 | + "scale='min(320,iw)':min'(320,ih)':force_original_aspect_ratio=decrease,fps=15, pad=320:320:-1:-1:[email protected], split [a][b]; [a] palettegen=reserve_transparent=on:transparency_color=ffffff [p]; [b][p] paletteuse", |
| 76 | + "-loop", |
| 77 | + "0", |
| 78 | + "-ss", |
| 79 | + "00:00:00", |
| 80 | + "-t", |
| 81 | + "00:00:05", |
| 82 | + "-preset", |
| 83 | + "default", |
| 84 | + "-an", |
| 85 | + "-vsync", |
| 86 | + "0" |
| 87 | + ]) |
| 88 | + .toFormat("webp") |
| 89 | + .save(tmpFileOut) |
| 90 | + }) |
| 91 | + |
| 92 | + const buff = fs.readFileSync(tmpFileOut) |
| 93 | + fs.unlinkSync(tmpFileOut) |
| 94 | + fs.unlinkSync(tmpFileIn) |
| 95 | + return buff |
| 96 | +} |
| 97 | + |
| 98 | +async function writeExifImg (media, metadata) { |
| 99 | + let wMedia = await imageToWebp(media) |
| 100 | + const tmpFileIn = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 101 | + const tmpFileOut = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 102 | + fs.writeFileSync(tmpFileIn, wMedia) |
| 103 | + |
| 104 | + if (metadata.packname || metadata.author) { |
| 105 | + const img = new webp.Image() |
| 106 | + const json = { "sticker-pack-id": `https://github.com/DGXeon`, "sticker-pack-name": metadata.packname, "sticker-pack-publisher": metadata.author, "emojis": metadata.categories ? metadata.categories : [""] } |
| 107 | + 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]) |
| 108 | + const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8") |
| 109 | + const exif = Buffer.concat([exifAttr, jsonBuff]) |
| 110 | + exif.writeUIntLE(jsonBuff.length, 14, 4) |
| 111 | + await img.load(tmpFileIn) |
| 112 | + fs.unlinkSync(tmpFileIn) |
| 113 | + img.exif = exif |
| 114 | + await img.save(tmpFileOut) |
| 115 | + return tmpFileOut |
| 116 | + } |
| 117 | +} |
| 118 | + |
| 119 | +async function writeExifVid (media, metadata) { |
| 120 | + let wMedia = await videoToWebp(media) |
| 121 | + const tmpFileIn = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 122 | + const tmpFileOut = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 123 | + fs.writeFileSync(tmpFileIn, wMedia) |
| 124 | + |
| 125 | + if (metadata.packname || metadata.author) { |
| 126 | + const img = new webp.Image() |
| 127 | + const json = { "sticker-pack-id": `https://github.com/DGXeon`, "sticker-pack-name": metadata.packname, "sticker-pack-publisher": metadata.author, "emojis": metadata.categories ? metadata.categories : [""] } |
| 128 | + 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]) |
| 129 | + const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8") |
| 130 | + const exif = Buffer.concat([exifAttr, jsonBuff]) |
| 131 | + exif.writeUIntLE(jsonBuff.length, 14, 4) |
| 132 | + await img.load(tmpFileIn) |
| 133 | + fs.unlinkSync(tmpFileIn) |
| 134 | + img.exif = exif |
| 135 | + await img.save(tmpFileOut) |
| 136 | + return tmpFileOut |
| 137 | + } |
| 138 | +} |
| 139 | + |
| 140 | +async function writeExif (media, metadata) { |
| 141 | + let wMedia = /webp/.test(media.mimetype) ? media.data : /image/.test(media.mimetype) ? await imageToWebp(media.data) : /video/.test(media.mimetype) ? await videoToWebp(media.data) : "" |
| 142 | + const tmpFileIn = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 143 | + const tmpFileOut = path.join(tmpdir(), `${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`) |
| 144 | + fs.writeFileSync(tmpFileIn, wMedia) |
| 145 | + |
| 146 | + if (metadata.packname || metadata.author) { |
| 147 | + const img = new webp.Image() |
| 148 | + const json = { "sticker-pack-id": `https://github.com/DGXeon`, "sticker-pack-name": metadata.packname, "sticker-pack-publisher": metadata.author, "emojis": metadata.categories ? metadata.categories : [""] } |
| 149 | + 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]) |
| 150 | + const jsonBuff = Buffer.from(JSON.stringify(json), "utf-8") |
| 151 | + const exif = Buffer.concat([exifAttr, jsonBuff]) |
| 152 | + exif.writeUIntLE(jsonBuff.length, 14, 4) |
| 153 | + await img.load(tmpFileIn) |
| 154 | + fs.unlinkSync(tmpFileIn) |
| 155 | + img.exif = exif |
| 156 | + await img.save(tmpFileOut) |
| 157 | + return tmpFileOut |
| 158 | + } |
| 159 | +} |
| 160 | +const { Image } = require('node-webpmux'); |
| 161 | +const { format } = require('util'); |
| 162 | + |
| 163 | +async function addExif(buffer, packname, author, categories = [''], extra = {}) { |
| 164 | + const img = new Image(); |
| 165 | + const json = { 'sticker-pack-id': 'DGXeon', 'sticker-pack-name': packname, 'sticker-pack-publisher': author, 'emojis': categories, 'is-avatar-sticker': 1, ...extra }; |
| 166 | + let 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]); |
| 167 | + let jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8'); |
| 168 | + let exif = Buffer.concat([exifAttr, jsonBuffer]); |
| 169 | + exif.writeUIntLE(jsonBuffer.length, 14, 4); |
| 170 | + await img.load(buffer); |
| 171 | + img.exif = exif; |
| 172 | + return await img.save(null); |
| 173 | +} |
| 174 | + |
| 175 | +module.exports = { addExif, imageToWebp, videoToWebp, writeExifImg, writeExifVid, writeExif } |
0 commit comments