@boruto_vk7/stickengine 0.0.3-alpha → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,135 +1,121 @@
1
- # StickEngine
1
+ # 🚀 StickEngine
2
2
 
3
- [![npm](https://img.shields.io/npm/v/@boruto_vk7/stickengine)](https://www.npmjs.com/package/@boruto_vk7/stickengine)
4
- [![license](https://img.shields.io/npm/l/@boruto_vk7/stickengine)](https://github.com/Borutovk7/StickEngine/blob/main/LICENSE)
3
+ **StickEngine** é um motor poderoso e leve para criação de figurinhas do WhatsApp (estáticas e animadas) com suporte integrado a filtros de imagem, adição de texto e remoção de fundo. Projetado para funcionar em qualquer lugar, do **Termux** a servidores dedicados.
5
4
 
6
- Módulo Node.js para criação de stickers para WhatsApp — feito pra funcionar em qualquer lugar, do **Termux no seu celular** até um servidor dedicado. Sem dependências nativas complicadas, sem dor de cabeça na instalação.
5
+ [![NPM Version](https://img.shields.io/npm/v/@boruto_vk7/stickengine?style=flat-square)](https://www.npmjs.com/package/@boruto_vk7/stickengine)
6
+ [![License](https://img.shields.io/npm/l/@boruto_vk7/stickengine?style=flat-square)](https://github.com/Borutovk7/StickEngine)
7
7
 
8
- > Sabe aquele sofrimento de tentar rodar um módulo de sticker no Termux e quebrar tudo por causa do `sharp` ou de alguma lib nativa? O StickEngine foi criado exatamente pra resolver isso. Aqui funciona.
8
+ ---
9
9
 
10
- ## Por que o StickEngine?
10
+ ## Funcionalidades
11
11
 
12
- - **Funciona no Termux** sem `sharp`, sem compilação nativa, sem erro de permissão
13
- - **Funciona em VPS, Replit, Railway, servidor dedicado** em qualquer ambiente
14
- - **Zero configuração extra** instala e já usa
15
- - **Suporte a ESM e CJS** compatível com qualquer projeto
16
- - **Entrada flexível** URL, Buffer ou caminho local, tanto faz
12
+ - 🎞️ **Figurinhas Animadas**: Suporte automático para GIFs e Vídeos (MP4).
13
+ - 🎨 **Filtros Profissionais**: Sépia, Tons de Cinza, Inversão e Desfoque (Blur).
14
+ - ✍️ **Texto Customizado**: Adicione legendas diretamente na figurinha.
15
+ - ✂️ **Remoção de Fundo**: Integração com a API `remove.bg`.
16
+ - 🧹 **Limpeza Automática**: Sistema de gerenciamento de arquivos temporários com o método `.clean()`.
17
+ - 🏷️ **Metadados (Exif)**: Personalize o nome do pacote e o autor sem complicação.
18
+ - 📱 **Termux Friendly**: Sem dependências nativas pesadas como `sharp`.
17
19
 
18
- ## Requisitos
20
+ ---
19
21
 
20
- - Node.js 16+
21
- - [ffmpeg](https://ffmpeg.org/) instalado no sistema
22
+ ## 🖼️ Exemplos Visuais
22
23
 
23
- ```bash
24
- # Termux
25
- pkg install ffmpeg
24
+ | Filtro Séia + Texto | Figurinha Animada (Mockup) |
25
+ | :---: | :---: |
26
+ | ![Sepia Preview](assets/preview_sepia.png) | ![Animated Preview](assets/preview_animated.png) |
26
27
 
27
- # Debian/Ubuntu/VPS
28
- sudo apt install ffmpeg
29
- ```
28
+ ---
30
29
 
31
- ## Instalação
30
+ ## 📦 Instalação
32
31
 
33
32
  ```bash
34
33
  npm install @boruto_vk7/stickengine
35
34
  ```
36
35
 
37
- ## Uso
36
+ > **Requisito:** Certifique-se de ter o [FFmpeg](https://ffmpeg.org/) instalado no seu sistema. No Termux: `pkg install ffmpeg`.
37
+
38
+ ---
38
39
 
39
- ### ESM
40
+ ## 🚀 Como Usar (Exemplos Técnicos)
40
41
 
42
+ ### 1. Criando uma Figurinha Estática Simples
41
43
  ```javascript
42
44
  import StickEngine from '@boruto_vk7/stickengine';
43
45
 
44
- const engine = new StickEngine({
45
- metadata: {
46
- pack: 'Meu Pack de Stickers',
47
- author: 'Borutovk7',
48
- emojis: ['✨', '🚀']
49
- },
50
- // Opcional: chaves da API remove.bg para remoção de fundo
51
- // transparent: ['SUA_API_KEY']
52
- });
46
+ const engine = new StickEngine();
47
+ engine.addFile('https://exemplo.com/foto.jpg');
53
48
 
54
- engine.on('st.start', (data) => console.log(`Processando:`, data.input));
55
- engine.on('st.data', (data) => console.log(`Sticker gerado:`, data.file));
56
- engine.on('st.error', (data) => console.error(`Erro:`, data.error));
49
+ const results = await engine.start();
50
+ console.log('Caminho do WebP:', results[0].value);
51
+ engine.clean();
52
+ ```
57
53
 
58
- const results = await engine
59
- .addFile('https://picsum.photos/512') // URL
60
- .addFile('caminho/para/video.mp4') // caminho local
61
- .addFile(buffer) // Buffer
62
- .start();
54
+ ### 2. Figurinha Animada (GIF ou Vídeo)
55
+ O motor detecta automaticamente a duração e converte para WebP animado.
56
+ ```javascript
57
+ const engine = new StickEngine({
58
+ fps: 20, // Aumenta a fluidez
59
+ quality: 70 // Otimiza o tamanho para o WhatsApp
60
+ });
63
61
 
64
- console.log(results);
62
+ engine.addFile('./video_engracado.mp4');
63
+ await engine.start();
64
+ engine.clean();
65
65
  ```
66
66
 
67
- ### CJS
68
-
67
+ ### 3. Aplicando Filtros e Texto
69
68
  ```javascript
70
- const StickEngine = require('@boruto_vk7/stickengine');
71
-
72
69
  const engine = new StickEngine({
73
- metadata: {
74
- pack: 'Meu Pack',
75
- author: 'Borutovk7',
76
- emojis: ['🎉']
70
+ edit: {
71
+ sepia: true,
72
+ blur: 5,
73
+ text: {
74
+ content: 'MUITO BOM!',
75
+ alignmentY: 2 // 0: Topo, 1: Meio, 2: Baixo
76
+ }
77
77
  }
78
78
  });
79
79
 
80
- engine.on('st.start', (data) => console.log(`Processando:`, data.input));
81
- engine.on('st.data', (data) => console.log(`Sticker gerado:`, data.file));
82
- engine.on('st.error', (data) => console.error(`Erro:`, data.error));
83
-
84
- engine.addFile('https://picsum.photos/200')
85
- .start()
86
- .then(results => console.log(results))
87
- .catch(err => console.error(err));
80
+ engine.addFile(bufferDeImagem);
81
+ await engine.start();
82
+ engine.clean();
88
83
  ```
89
84
 
90
- ## Eventos
91
-
92
- | Evento | Descrição |
93
- |--------|-----------|
94
- | `st.start` | Disparado ao iniciar o processamento de um arquivo |
95
- | `st.info` | Metadados do arquivo sendo processado |
96
- | `st.data` | Sticker gerado com sucesso |
97
- | `st.error` | Erro ao processar um arquivo |
98
-
99
- ## API
100
-
101
- ### `new StickEngine(options)`
102
-
103
- | Opção | Tipo | Descrição |
104
- |-------|------|-----------|
105
- | `metadata.pack` | `string` | Nome do pacote de stickers |
106
- | `metadata.author` | `string` | Nome do autor |
107
- | `metadata.emojis` | `string[]` | Emojis associados ao sticker |
108
- | `transparent` | `string[]` | Chaves da API remove.bg para remoção de fundo |
109
-
110
- ### `.addFile(input)`
111
-
112
- Adiciona um arquivo à fila. Aceita URL, Buffer ou caminho local. Retorna a instância para encadeamento.
85
+ ### 4. Remoção de Fundo (Remove.bg)
86
+ ```javascript
87
+ const engine = new StickEngine({
88
+ transparent: 'SUA_API_KEY_AQUI'
89
+ });
113
90
 
114
- ### `.start()`
91
+ engine.addFile('foto_com_fundo.png');
92
+ await engine.start();
93
+ engine.clean();
94
+ ```
115
95
 
116
- Inicia o processamento da fila. Retorna uma Promise com os resultados.
96
+ ---
117
97
 
118
- ### `getBuffer(url)`
98
+ ## ⚙️ Opções do Construtor
119
99
 
120
- Utilitário para baixar uma URL como Buffer.
100
+ | Opção | Tipo | Padrão | Descrição |
101
+ | :--- | :--- | :--- | :--- |
102
+ | `metadata` | `Object` | `{ pack: 'StickEngine', author: 'Borutovk7' }` | Nome do pacote e autor. |
103
+ | `edit` | `Object` | `false` | Filtros (`sepia`, `greyscale`, `invert`, `blur`) e `text`. |
104
+ | `transparent` | `String` | `false` | Chave da API `remove.bg` para remover fundo. |
105
+ | `autoClean` | `Boolean` | `true` | Habilita o rastreio de arquivos para limpeza. |
106
+ | `quality` | `Number` | `80` | Qualidade do WebP (1-100). |
107
+ | `fps` | `Number` | `15` | Frames por segundo para animações. |
121
108
 
122
- ```javascript
123
- import { getBuffer } from '@boruto_vk7/stickengine';
109
+ ---
124
110
 
125
- const buffer = await getBuffer('https://example.com/image.png');
126
- ```
111
+ ## 🤝 Contribuições
127
112
 
128
- ## Créditos
113
+ Contribuições são sempre bem-vindas! Sinta-se à vontade para abrir uma **Issue** ou enviar um **Pull Request**.
129
114
 
130
- - [Eduh dev](https://github.com/Borutovk7)
115
+ Desenvolvido por [Borutovk7](https://github.com/Borutovk7) e [Eduh Dev](https://github.com/EduhDev).
131
116
 
117
+ ---
132
118
 
133
- ## Licença
119
+ ## 📄 Licença
134
120
 
135
- ISC
121
+ Este projeto está sob a licença [ISC](LICENSE).
Binary file
Binary file
@@ -5,25 +5,46 @@ export interface StickerMetadata {
5
5
  author?: string;
6
6
  emojis?: string[];
7
7
  }
8
+ export interface JimpOptions {
9
+ greyscale?: boolean;
10
+ invert?: boolean;
11
+ sepia?: boolean;
12
+ blur?: number;
13
+ resize?: {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ text?: {
18
+ content: string;
19
+ alignmentX?: number;
20
+ alignmentY?: number;
21
+ color?: string;
22
+ };
23
+ }
8
24
  export interface StickEngineOptions {
9
25
  webp?: boolean;
10
- edit?: string | boolean;
26
+ edit?: JimpOptions | boolean;
11
27
  convert?: boolean;
12
28
  transparent?: string | string[] | false;
13
29
  metadata?: StickerMetadata;
30
+ autoClean?: boolean;
31
+ fps?: number;
32
+ quality?: number;
14
33
  }
15
34
  /**
16
35
  * Faz GET em uma URL e retorna o corpo como Buffer.
17
- * Fornecido pelo usuário para integração.
18
36
  */
19
37
  export declare const getBuffer: (url: string, options?: AxiosRequestConfig) => Promise<Buffer>;
20
38
  export declare class StickEngine extends EventEmitter {
21
39
  options: StickEngineOptions;
22
40
  private tempDir;
23
41
  private queue;
42
+ private createdFiles;
24
43
  constructor(options?: StickEngineOptions);
25
44
  addFile(file: string | Buffer): this;
45
+ clean(): void;
26
46
  private processFile;
47
+ private applyJimpEdits;
27
48
  private getMediaInfo;
28
49
  private convertToWebp;
29
50
  private addExif;
@@ -3,18 +3,14 @@ import path from 'path';
3
3
  import axios from 'axios';
4
4
  import { exec } from 'child_process';
5
5
  import { EventEmitter } from 'events';
6
- import ffmpeg from 'fluent-ffmpeg';
7
- // @ts-ignore
8
6
  import webpMux from 'node-webpmux';
9
- // @ts-ignore
7
+ import Jimp from 'jimp';
10
8
  import { removeBackgroundFromImageFile } from 'remove.bg';
11
9
  import { fileURLToPath } from 'url';
10
+
12
11
  const __filename = fileURLToPath(import.meta.url);
13
12
  const __dirname = path.dirname(__filename);
14
- /**
15
- * Faz GET em uma URL e retorna o corpo como Buffer.
16
- * Fornecido pelo usuário para integração.
17
- */
13
+
18
14
  export const getBuffer = async (url, options = {}) => {
19
15
  try {
20
16
  const { data } = await axios({
@@ -32,17 +28,20 @@ export const getBuffer = async (url, options = {}) => {
32
28
  }
33
29
  catch (err) {
34
30
  console.error(`Erro em getBuffer: ${err}`);
35
- const errorImagePath = path.join(__dirname, 'src', 'emror.jpg');
31
+ const errorImagePath = path.join(__dirname, 'emror.jpg');
36
32
  if (fs.existsSync(errorImagePath)) {
37
33
  return fs.readFileSync(errorImagePath);
38
34
  }
39
35
  return Buffer.alloc(0);
40
36
  }
41
37
  };
38
+
42
39
  export class StickEngine extends EventEmitter {
43
40
  options;
44
41
  tempDir;
45
42
  queue;
43
+ createdFiles;
44
+
46
45
  constructor(options = {}) {
47
46
  super();
48
47
  this.options = {
@@ -50,6 +49,9 @@ export class StickEngine extends EventEmitter {
50
49
  edit: false,
51
50
  convert: false,
52
51
  transparent: false,
52
+ autoClean: true,
53
+ fps: 15,
54
+ quality: 80,
53
55
  metadata: {
54
56
  pack: 'StickEngine',
55
57
  author: 'Borutovk7 & Eduh Dev',
@@ -62,24 +64,44 @@ export class StickEngine extends EventEmitter {
62
64
  fs.mkdirSync(this.tempDir, { recursive: true });
63
65
  }
64
66
  this.queue = [];
67
+ this.createdFiles = [];
65
68
  }
69
+
66
70
  addFile(file) {
67
71
  this.queue.push(file);
68
72
  return this;
69
73
  }
74
+
75
+ clean() {
76
+ for (const file of this.createdFiles) {
77
+ try {
78
+ if (fs.existsSync(file)) {
79
+ fs.unlinkSync(file);
80
+ }
81
+ }
82
+ catch (err) {
83
+ console.error(`Erro ao deletar arquivo temporário ${file}:`, err);
84
+ }
85
+ }
86
+ this.createdFiles = [];
87
+ }
88
+
70
89
  async processFile(input, index) {
71
- let filePath;
72
- let extension;
90
+ let filePath = '';
91
+ let extension = '';
73
92
  const timestamp = Date.now();
93
+ const localCreatedFiles = [];
74
94
  try {
75
95
  this.emit('st.start', { index, input });
76
96
  const ft = await import('file-type');
77
97
  const fileTypeFunc = ft.fromBuffer || ft.fileTypeFromBuffer || (ft.default && (ft.default.fromBuffer || ft.default.fileTypeFromBuffer));
98
+
78
99
  if (Buffer.isBuffer(input)) {
79
100
  const type = await fileTypeFunc(input);
80
101
  extension = type ? type.ext : 'bin';
81
102
  filePath = path.join(this.tempDir, `input_${timestamp}_${index}.${extension}`);
82
103
  fs.writeFileSync(filePath, input);
104
+ localCreatedFiles.push(filePath);
83
105
  }
84
106
  else if (typeof input === 'string' && input.startsWith('http')) {
85
107
  const buffer = await getBuffer(input);
@@ -87,18 +109,28 @@ export class StickEngine extends EventEmitter {
87
109
  extension = type ? type.ext : 'bin';
88
110
  filePath = path.join(this.tempDir, `input_${timestamp}_${index}.${extension}`);
89
111
  fs.writeFileSync(filePath, buffer);
112
+ localCreatedFiles.push(filePath);
90
113
  }
91
114
  else if (typeof input === 'string' && fs.existsSync(input)) {
92
- extension = path.extname(input).slice(1);
93
115
  filePath = input;
94
116
  }
95
117
  else {
96
118
  throw new Error('Tipo de entrada inválido ou arquivo não encontrado.');
97
119
  }
98
- const metadata = await this.getMediaInfo(filePath);
99
- this.emit('st.info', { index, ...metadata });
120
+
121
+ const mediaInfo = await this.getMediaInfo(filePath);
122
+ this.emit('st.info', { index, ...mediaInfo });
100
123
  let currentFile = filePath;
101
- if (this.options.transparent) {
124
+ const isAnimated = mediaInfo.duration > 0 || extension === 'gif' || extension === 'mp4' || extension === 'webp';
125
+
126
+ if (this.options.edit && !isAnimated) {
127
+ const editedPath = path.join(this.tempDir, `edited_${timestamp}_${index}.png`);
128
+ await this.applyJimpEdits(currentFile, editedPath, this.options.edit);
129
+ localCreatedFiles.push(editedPath);
130
+ currentFile = editedPath;
131
+ }
132
+
133
+ if (this.options.transparent && !isAnimated) {
102
134
  const apiKey = Array.isArray(this.options.transparent)
103
135
  ? this.options.transparent[Math.floor(Math.random() * this.options.transparent.length)]
104
136
  : this.options.transparent;
@@ -111,78 +143,117 @@ export class StickEngine extends EventEmitter {
111
143
  type: 'auto'
112
144
  });
113
145
  fs.writeFileSync(bgRemovedPath, Buffer.from(result.base64img, 'base64'));
146
+ localCreatedFiles.push(bgRemovedPath);
114
147
  currentFile = bgRemovedPath;
115
148
  }
116
149
  }
150
+
117
151
  const webpPath = path.join(this.tempDir, `sticker_${timestamp}_${index}.webp`);
118
- await this.convertToWebp(currentFile, webpPath);
152
+ await this.convertToWebp(currentFile, webpPath, isAnimated);
153
+ localCreatedFiles.push(webpPath);
119
154
  currentFile = webpPath;
155
+
120
156
  if (this.options.metadata) {
121
157
  await this.addExif(currentFile, this.options.metadata);
122
158
  }
159
+
160
+ this.createdFiles.push(...localCreatedFiles);
123
161
  this.emit('st.data', { index, file: currentFile });
124
162
  return currentFile;
125
163
  }
126
164
  catch (error) {
127
165
  this.emit('st.error', { index, error: error.message });
166
+ for (const f of localCreatedFiles) {
167
+ if (fs.existsSync(f))
168
+ fs.unlinkSync(f);
169
+ }
128
170
  throw error;
129
171
  }
130
172
  }
173
+
174
+ async applyJimpEdits(input, output, options) {
175
+ const image = await Jimp.read(input);
176
+ if (options.greyscale) image.greyscale();
177
+ if (options.invert) image.invert();
178
+ if (options.sepia) image.sepia();
179
+ if (options.blur) image.blur(options.blur);
180
+ if (options.resize) image.resize(options.resize.width, options.resize.height);
181
+ if (options.text) {
182
+ const font = await Jimp.loadFont(Jimp.FONT_SANS_32_WHITE);
183
+ image.print(font, 0, 0, {
184
+ text: options.text.content,
185
+ alignmentX: options.text.alignmentX || Jimp.HORIZONTAL_ALIGN_CENTER,
186
+ alignmentY: options.text.alignmentY || Jimp.VERTICAL_ALIGN_BOTTOM
187
+ }, image.bitmap.width, image.bitmap.height);
188
+ }
189
+ await image.writeAsync(output);
190
+ }
191
+
131
192
  getMediaInfo(file) {
132
- return new Promise((resolve, reject) => {
133
- exec(`ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration -of csv=p=0:s=x ${file}`, (err, stdout) => {
134
- if (err)
135
- return reject(err);
136
- const [width, height, duration] = stdout.split('x');
193
+ return new Promise((resolve) => {
194
+ exec(`ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration -of csv=p=0:s=x "${file}"`, (err, stdout) => {
195
+ if (err) return resolve({ width: 0, height: 0, duration: 0 });
196
+ const parts = stdout.trim().split('x');
137
197
  resolve({
138
- width: parseInt(width) || 0,
139
- height: parseInt(height) || 0,
140
- duration: parseFloat(duration) || 0
198
+ width: parseInt(parts[0]) || 0,
199
+ height: parseInt(parts[1]) || 0,
200
+ duration: parseFloat(parts[2]) || 0
141
201
  });
142
202
  });
143
203
  });
144
204
  }
145
- convertToWebp(input, output) {
205
+
206
+ convertToWebp(input, output, isAnimated) {
146
207
  return new Promise((resolve, reject) => {
147
- ffmpeg(input)
148
- .on('error', reject)
149
- .on('end', () => resolve())
150
- .addOutputOptions([
151
- '-vcodec', 'libwebp',
152
- '-vf', 'scale=512:512:force_original_aspect_ratio=decrease,fps=15,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1',
153
- '-lossless', '1',
154
- '-loop', '0',
155
- '-preset', 'default',
156
- '-an',
157
- '-vsync', '0'
158
- ])
159
- .toFormat('webp')
160
- .save(output);
208
+ const fps = this.options.fps;
209
+ const quality = this.options.quality;
210
+ const scale = `scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1`;
211
+
212
+ let cmd;
213
+ if (isAnimated) {
214
+ cmd = `ffmpeg -y -i "${input}" -vcodec libwebp -vf "fps=${fps},${scale}" -lossless 0 -q:v ${quality} -loop 0 -preset default -an -vsync 0 -t 6 "${output}"`;
215
+ } else {
216
+ cmd = `ffmpeg -y -i "${input}" -vcodec libwebp -vf "${scale}" -lossless 1 -loop 0 -preset default -an -vsync 0 "${output}"`;
217
+ }
218
+
219
+ exec(cmd, (err, stdout, stderr) => {
220
+ if (err) return reject(new Error(stderr || err.message));
221
+ resolve();
222
+ });
161
223
  });
162
224
  }
225
+
163
226
  async addExif(webpPath, metadata) {
164
- const json = {
165
- 'sticker-pack-id': 'StickEngine-Manus',
166
- 'sticker-pack-name': metadata.pack || 'StickEngine',
167
- 'sticker-pack-publisher': metadata.author || 'Borutovk7 & Eduh Dev',
168
- 'emojis': metadata.emojis || ['🥶']
169
- };
170
- const Image = webpMux.Image || (webpMux.default && webpMux.default.Image);
171
- const img = new Image();
172
- 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]);
173
- const jsonBuff = Buffer.from(JSON.stringify(json), 'utf-8');
174
- const exifBuffer = Buffer.concat([exifAttr, jsonBuff]);
175
- exifBuffer.writeUIntLE(jsonBuff.length, 14, 4);
176
- await img.load(webpPath);
177
- img.exif = exifBuffer;
178
- await img.save(webpPath);
227
+ const json = {
228
+ 'sticker-pack-id': 'StickEngine-Official',
229
+ 'sticker-pack-name': metadata.pack || 'StickEngine',
230
+ 'sticker-pack-publisher': metadata.author || 'Borutovk7',
231
+ 'emojis': metadata.emojis || ['🥶']
232
+ };
233
+
234
+ 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]);
235
+ const jsonBuff = Buffer.from(JSON.stringify(json), 'utf-8');
236
+ const exifBuffer = Buffer.concat([exifAttr, jsonBuff]);
237
+ exifBuffer.writeUIntLE(jsonBuff.length, 14, 4);
238
+
239
+ const { Image } = webpMux;
240
+ const img = new Image();
241
+ await img.load(webpPath);
242
+ img.exif = exifBuffer;
243
+
244
+ if (img.hasAnim) {
245
+ await img.muxAnim({ path: webpPath });
246
+ } else {
247
+ await Image.save(webpPath, img);
179
248
  }
249
+ }
250
+
180
251
  async start() {
181
- if (this.queue.length === 0)
182
- throw new Error('Nenhum arquivo na fila.');
252
+ if (this.queue.length === 0) throw new Error('Nenhum arquivo na fila.');
183
253
  const results = await Promise.allSettled(this.queue.map((file, i) => this.processFile(file, i)));
184
254
  this.queue = [];
185
255
  return results;
186
256
  }
187
257
  }
188
- export default StickEngine;
258
+
259
+ export default StickEngine;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boruto_vk7/stickengine",
3
- "version": "0.0.3-alpha",
3
+ "version": "0.0.5",
4
4
  "description": "StickEngine module for creating WhatsApp stickers",
5
5
  "type": "module",
6
6
  "main": "dist/StickEngine.js",