@boruto_vk7/stickengine 0.0.2-alpha → 0.0.4
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 +78 -92
- package/assets/preview_animated.png +0 -0
- package/assets/preview_sepia.png +0 -0
- package/dist/StickEngine.d.ts +23 -2
- package/dist/StickEngine.js +90 -23
- package/package.json +1 -1
- package/README.md.bak +0 -131
- package/package.json.bak +0 -49
- package/src/StickEngine.ts +0 -221
- package/src/emror.jpg +0 -0
- package/tsconfig.json +0 -18
package/README.md
CHANGED
|
@@ -1,135 +1,121 @@
|
|
|
1
|
-
# StickEngine
|
|
1
|
+
# 🚀 StickEngine
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](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
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@boruto_vk7/stickengine)
|
|
6
|
+
[](https://github.com/Borutovk7/StickEngine)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
---
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## ✨ Funcionalidades
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
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
|
-
|
|
20
|
+
---
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
- [ffmpeg](https://ffmpeg.org/) instalado no sistema
|
|
22
|
+
## 🖼️ Exemplos Visuais
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
| Filtro Séia + Texto | Figurinha Animada (Mockup) |
|
|
25
|
+
| :---: | :---: |
|
|
26
|
+
|  |  |
|
|
26
27
|
|
|
27
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
engine.
|
|
49
|
+
const results = await engine.start();
|
|
50
|
+
console.log('Caminho do WebP:', results[0].value);
|
|
51
|
+
engine.clean();
|
|
52
|
+
```
|
|
57
53
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
62
|
+
engine.addFile('./video_engracado.mp4');
|
|
63
|
+
await engine.start();
|
|
64
|
+
engine.clean();
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
###
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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.
|
|
81
|
-
engine.
|
|
82
|
-
engine.
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
91
|
+
engine.addFile('foto_com_fundo.png');
|
|
92
|
+
await engine.start();
|
|
93
|
+
engine.clean();
|
|
94
|
+
```
|
|
115
95
|
|
|
116
|
-
|
|
96
|
+
---
|
|
117
97
|
|
|
118
|
-
|
|
98
|
+
## ⚙️ Opções do Construtor
|
|
119
99
|
|
|
120
|
-
|
|
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
|
-
|
|
123
|
-
import { getBuffer } from '@boruto_vk7/stickengine';
|
|
109
|
+
---
|
|
124
110
|
|
|
125
|
-
|
|
126
|
-
```
|
|
111
|
+
## 🤝 Contribuições
|
|
127
112
|
|
|
128
|
-
|
|
113
|
+
Contribuições são sempre bem-vindas! Sinta-se à vontade para abrir uma **Issue** ou enviar um **Pull Request**.
|
|
129
114
|
|
|
130
|
-
|
|
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
|
package/dist/StickEngine.d.ts
CHANGED
|
@@ -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?:
|
|
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;
|
package/dist/StickEngine.js
CHANGED
|
@@ -7,13 +7,14 @@ import ffmpeg from 'fluent-ffmpeg';
|
|
|
7
7
|
// @ts-ignore
|
|
8
8
|
import webpMux from 'node-webpmux';
|
|
9
9
|
// @ts-ignore
|
|
10
|
+
import Jimp from 'jimp';
|
|
11
|
+
// @ts-ignore
|
|
10
12
|
import { removeBackgroundFromImageFile } from 'remove.bg';
|
|
11
13
|
import { fileURLToPath } from 'url';
|
|
12
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
15
|
const __dirname = path.dirname(__filename);
|
|
14
16
|
/**
|
|
15
17
|
* Faz GET em uma URL e retorna o corpo como Buffer.
|
|
16
|
-
* Fornecido pelo usuário para integração.
|
|
17
18
|
*/
|
|
18
19
|
export const getBuffer = async (url, options = {}) => {
|
|
19
20
|
try {
|
|
@@ -32,7 +33,7 @@ export const getBuffer = async (url, options = {}) => {
|
|
|
32
33
|
}
|
|
33
34
|
catch (err) {
|
|
34
35
|
console.error(`Erro em getBuffer: ${err}`);
|
|
35
|
-
const errorImagePath = path.join(__dirname, '
|
|
36
|
+
const errorImagePath = path.join(__dirname, 'emror.jpg');
|
|
36
37
|
if (fs.existsSync(errorImagePath)) {
|
|
37
38
|
return fs.readFileSync(errorImagePath);
|
|
38
39
|
}
|
|
@@ -43,6 +44,7 @@ export class StickEngine extends EventEmitter {
|
|
|
43
44
|
options;
|
|
44
45
|
tempDir;
|
|
45
46
|
queue;
|
|
47
|
+
createdFiles;
|
|
46
48
|
constructor(options = {}) {
|
|
47
49
|
super();
|
|
48
50
|
this.options = {
|
|
@@ -50,6 +52,9 @@ export class StickEngine extends EventEmitter {
|
|
|
50
52
|
edit: false,
|
|
51
53
|
convert: false,
|
|
52
54
|
transparent: false,
|
|
55
|
+
autoClean: true,
|
|
56
|
+
fps: 15,
|
|
57
|
+
quality: 80,
|
|
53
58
|
metadata: {
|
|
54
59
|
pack: 'StickEngine',
|
|
55
60
|
author: 'Borutovk7 & Eduh Dev',
|
|
@@ -62,15 +67,30 @@ export class StickEngine extends EventEmitter {
|
|
|
62
67
|
fs.mkdirSync(this.tempDir, { recursive: true });
|
|
63
68
|
}
|
|
64
69
|
this.queue = [];
|
|
70
|
+
this.createdFiles = [];
|
|
65
71
|
}
|
|
66
72
|
addFile(file) {
|
|
67
73
|
this.queue.push(file);
|
|
68
74
|
return this;
|
|
69
75
|
}
|
|
76
|
+
clean() {
|
|
77
|
+
for (const file of this.createdFiles) {
|
|
78
|
+
try {
|
|
79
|
+
if (fs.existsSync(file)) {
|
|
80
|
+
fs.unlinkSync(file);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error(`Erro ao deletar arquivo temporário ${file}:`, err);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this.createdFiles = [];
|
|
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');
|
|
@@ -80,6 +100,7 @@ export class StickEngine extends EventEmitter {
|
|
|
80
100
|
extension = type ? type.ext : 'bin';
|
|
81
101
|
filePath = path.join(this.tempDir, `input_${timestamp}_${index}.${extension}`);
|
|
82
102
|
fs.writeFileSync(filePath, input);
|
|
103
|
+
localCreatedFiles.push(filePath);
|
|
83
104
|
}
|
|
84
105
|
else if (typeof input === 'string' && input.startsWith('http')) {
|
|
85
106
|
const buffer = await getBuffer(input);
|
|
@@ -87,18 +108,27 @@ export class StickEngine extends EventEmitter {
|
|
|
87
108
|
extension = type ? type.ext : 'bin';
|
|
88
109
|
filePath = path.join(this.tempDir, `input_${timestamp}_${index}.${extension}`);
|
|
89
110
|
fs.writeFileSync(filePath, buffer);
|
|
111
|
+
localCreatedFiles.push(filePath);
|
|
90
112
|
}
|
|
91
113
|
else if (typeof input === 'string' && fs.existsSync(input)) {
|
|
92
|
-
extension = path.extname(input).slice(1);
|
|
93
114
|
filePath = input;
|
|
94
115
|
}
|
|
95
116
|
else {
|
|
96
117
|
throw new Error('Tipo de entrada inválido ou arquivo não encontrado.');
|
|
97
118
|
}
|
|
98
|
-
const
|
|
99
|
-
this.emit('st.info', { index, ...
|
|
119
|
+
const mediaInfo = await this.getMediaInfo(filePath);
|
|
120
|
+
this.emit('st.info', { index, ...mediaInfo });
|
|
100
121
|
let currentFile = filePath;
|
|
101
|
-
|
|
122
|
+
const isAnimated = mediaInfo.duration > 0 || extension === 'gif' || extension === 'mp4' || extension === 'webp';
|
|
123
|
+
// Aplicar Edições Jimp (Apenas para Imagens Estáticas por enquanto)
|
|
124
|
+
if (this.options.edit && !isAnimated) {
|
|
125
|
+
const editedPath = path.join(this.tempDir, `edited_${timestamp}_${index}.png`);
|
|
126
|
+
await this.applyJimpEdits(currentFile, editedPath, this.options.edit);
|
|
127
|
+
localCreatedFiles.push(editedPath);
|
|
128
|
+
currentFile = editedPath;
|
|
129
|
+
}
|
|
130
|
+
// Remoção de fundo (Apenas para Imagens Estáticas)
|
|
131
|
+
if (this.options.transparent && !isAnimated) {
|
|
102
132
|
const apiKey = Array.isArray(this.options.transparent)
|
|
103
133
|
? this.options.transparent[Math.floor(Math.random() * this.options.transparent.length)]
|
|
104
134
|
: this.options.transparent;
|
|
@@ -111,60 +141,97 @@ export class StickEngine extends EventEmitter {
|
|
|
111
141
|
type: 'auto'
|
|
112
142
|
});
|
|
113
143
|
fs.writeFileSync(bgRemovedPath, Buffer.from(result.base64img, 'base64'));
|
|
144
|
+
localCreatedFiles.push(bgRemovedPath);
|
|
114
145
|
currentFile = bgRemovedPath;
|
|
115
146
|
}
|
|
116
147
|
}
|
|
148
|
+
// Conversão para WebP (Suporta Estático e Animado)
|
|
117
149
|
const webpPath = path.join(this.tempDir, `sticker_${timestamp}_${index}.webp`);
|
|
118
|
-
await this.convertToWebp(currentFile, webpPath);
|
|
150
|
+
await this.convertToWebp(currentFile, webpPath, isAnimated);
|
|
151
|
+
localCreatedFiles.push(webpPath);
|
|
119
152
|
currentFile = webpPath;
|
|
153
|
+
// Adição de Metadados (Exif)
|
|
120
154
|
if (this.options.metadata) {
|
|
121
155
|
await this.addExif(currentFile, this.options.metadata);
|
|
122
156
|
}
|
|
157
|
+
this.createdFiles.push(...localCreatedFiles);
|
|
123
158
|
this.emit('st.data', { index, file: currentFile });
|
|
124
159
|
return currentFile;
|
|
125
160
|
}
|
|
126
161
|
catch (error) {
|
|
127
162
|
this.emit('st.error', { index, error: error.message });
|
|
163
|
+
for (const f of localCreatedFiles) {
|
|
164
|
+
if (fs.existsSync(f))
|
|
165
|
+
fs.unlinkSync(f);
|
|
166
|
+
}
|
|
128
167
|
throw error;
|
|
129
168
|
}
|
|
130
169
|
}
|
|
170
|
+
async applyJimpEdits(input, output, options) {
|
|
171
|
+
const image = await Jimp.read(input);
|
|
172
|
+
if (options.greyscale)
|
|
173
|
+
image.greyscale();
|
|
174
|
+
if (options.invert)
|
|
175
|
+
image.invert();
|
|
176
|
+
if (options.sepia)
|
|
177
|
+
image.sepia();
|
|
178
|
+
if (options.blur)
|
|
179
|
+
image.blur(options.blur);
|
|
180
|
+
if (options.resize)
|
|
181
|
+
image.resize(options.resize.width, options.resize.height);
|
|
182
|
+
if (options.text) {
|
|
183
|
+
const font = await Jimp.loadFont(Jimp.FONT_SANS_32_WHITE);
|
|
184
|
+
image.print(font, 0, 0, {
|
|
185
|
+
text: options.text.content,
|
|
186
|
+
alignmentX: options.text.alignmentX || Jimp.HORIZONTAL_ALIGN_CENTER,
|
|
187
|
+
alignmentY: options.text.alignmentY || Jimp.VERTICAL_ALIGN_BOTTOM
|
|
188
|
+
}, image.bitmap.width, image.bitmap.height);
|
|
189
|
+
}
|
|
190
|
+
await image.writeAsync(output);
|
|
191
|
+
}
|
|
131
192
|
getMediaInfo(file) {
|
|
132
|
-
return new Promise((resolve
|
|
193
|
+
return new Promise((resolve) => {
|
|
133
194
|
exec(`ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration -of csv=p=0:s=x ${file}`, (err, stdout) => {
|
|
134
195
|
if (err)
|
|
135
|
-
return
|
|
136
|
-
const
|
|
196
|
+
return resolve({ width: 0, height: 0, duration: 0 });
|
|
197
|
+
const parts = stdout.trim().split('x');
|
|
137
198
|
resolve({
|
|
138
|
-
width: parseInt(
|
|
139
|
-
height: parseInt(
|
|
140
|
-
duration: parseFloat(
|
|
199
|
+
width: parseInt(parts[0]) || 0,
|
|
200
|
+
height: parseInt(parts[1]) || 0,
|
|
201
|
+
duration: parseFloat(parts[2]) || 0
|
|
141
202
|
});
|
|
142
203
|
});
|
|
143
204
|
});
|
|
144
205
|
}
|
|
145
|
-
convertToWebp(input, output) {
|
|
206
|
+
convertToWebp(input, output, isAnimated) {
|
|
146
207
|
return new Promise((resolve, reject) => {
|
|
147
|
-
ffmpeg(input)
|
|
208
|
+
const ff = ffmpeg(input)
|
|
148
209
|
.on('error', reject)
|
|
149
|
-
.on('end', () => resolve())
|
|
150
|
-
|
|
210
|
+
.on('end', () => resolve());
|
|
211
|
+
const videoOptions = [
|
|
151
212
|
'-vcodec', 'libwebp',
|
|
152
|
-
'-vf',
|
|
213
|
+
'-vf', `scale=512:512:force_original_aspect_ratio=decrease,fps=${this.options.fps},pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1`,
|
|
153
214
|
'-lossless', '1',
|
|
154
215
|
'-loop', '0',
|
|
155
216
|
'-preset', 'default',
|
|
156
217
|
'-an',
|
|
157
218
|
'-vsync', '0'
|
|
158
|
-
]
|
|
219
|
+
];
|
|
220
|
+
if (isAnimated) {
|
|
221
|
+
// Opções específicas para animação para manter o peso baixo (limite do WhatsApp é 1MB)
|
|
222
|
+
videoOptions.push('-t', '6'); // Limita a 6 segundos
|
|
223
|
+
videoOptions.push('-q:v', String(this.options.quality));
|
|
224
|
+
}
|
|
225
|
+
ff.addOutputOptions(videoOptions)
|
|
159
226
|
.toFormat('webp')
|
|
160
227
|
.save(output);
|
|
161
228
|
});
|
|
162
229
|
}
|
|
163
230
|
async addExif(webpPath, metadata) {
|
|
164
231
|
const json = {
|
|
165
|
-
'sticker-pack-id': 'StickEngine-
|
|
232
|
+
'sticker-pack-id': 'StickEngine-Official',
|
|
166
233
|
'sticker-pack-name': metadata.pack || 'StickEngine',
|
|
167
|
-
'sticker-pack-publisher': metadata.author || 'Borutovk7
|
|
234
|
+
'sticker-pack-publisher': metadata.author || 'Borutovk7',
|
|
168
235
|
'emojis': metadata.emojis || ['🥶']
|
|
169
236
|
};
|
|
170
237
|
const Image = webpMux.Image || (webpMux.default && webpMux.default.Image);
|
package/package.json
CHANGED
package/README.md.bak
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
# StickEngine: Gerador de Stickers para WhatsApp
|
|
2
|
-
|
|
3
|
-
Bem-vindo ao **StickEngine**, um módulo Node.js robusto e flexível para a criação de stickers personalizados para WhatsApp. Desenvolvido para ser eficiente e compatível com diversos ambientes, o StickEngine permite transformar imagens e vídeos em stickers WebP com metadados personalizados, além de oferecer funcionalidades como remoção de fundo.
|
|
4
|
-
|
|
5
|
-
## Funcionalidades Principais
|
|
6
|
-
|
|
7
|
-
- **Geração de Stickers WebP**: Converte imagens (JPG, PNG, GIF) e vídeos (MP4, WebM) para o formato WebP, otimizado para stickers do WhatsApp.
|
|
8
|
-
- **Metadados Personalizados (EXIF)**: Adiciona informações como nome do pacote, autor e emojis diretamente no sticker.
|
|
9
|
-
- **Suporte a URLs, Buffers e Caminhos Locais**: Flexibilidade na entrada de dados para a criação dos stickers.
|
|
10
|
-
- **Remoção de Fundo (Opcional)**: Integração com a API `remove.bg` para remover o fundo de imagens, ideal para criar stickers transparentes.
|
|
11
|
-
- **Compatibilidade CJS e ESM**: O módulo pode ser utilizado tanto em projetos CommonJS (`require`) quanto em módulos ES (`import`).
|
|
12
|
-
|
|
13
|
-
## Instalação
|
|
14
|
-
|
|
15
|
-
Para utilizar o StickEngine, você precisará ter o Node.js e o `ffmpeg` instalados em seu sistema. O `ffmpeg` é essencial para a conversão de mídia.
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# Instale as dependências do Node.js
|
|
19
|
-
pnpm install axios fluent-ffmpeg node-webpmux file-type jimp remove.bg node-fetch
|
|
20
|
-
|
|
21
|
-
# Certifique-se de ter o ffmpeg instalado e acessível no PATH do seu sistema.
|
|
22
|
-
# Para sistemas baseados em Debian/Ubuntu:
|
|
23
|
-
sudo apt update && sudo apt install ffmpeg
|
|
24
|
-
# Para outras plataformas, consulte a documentação oficial do ffmpeg.
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Uso
|
|
28
|
-
|
|
29
|
-
O StickEngine é projetado para ser fácil de usar, com uma API baseada em classes e eventos para feedback de progresso.
|
|
30
|
-
|
|
31
|
-
### Exemplo de Uso (ESM - `index.js`)
|
|
32
|
-
|
|
33
|
-
```javascript
|
|
34
|
-
import StickEngine from './index.js';
|
|
35
|
-
|
|
36
|
-
const engine = new StickEngine({
|
|
37
|
-
metadata: {
|
|
38
|
-
pack: 'Meu Pack de Stickers',
|
|
39
|
-
author: 'Borutovk7 & Eduh Dev',
|
|
40
|
-
emojis: ['✨', '🚀'] // Emojis opcionais
|
|
41
|
-
},
|
|
42
|
-
// Para remover o fundo, forneça uma ou mais chaves de API do remove.bg
|
|
43
|
-
// transparent: ['SUA_API_KEY_REMOVEBG_1', 'SUA_API_KEY_REMOVEBG_2']
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// Eventos para acompanhar o progresso
|
|
47
|
-
engine.on('st.start', (data) => console.log(`[START] Processando arquivo ${data.index}:`, data.input));
|
|
48
|
-
engine.on('st.info', (data) => console.log(`[INFO] Metadados do arquivo ${data.index}:`, data));
|
|
49
|
-
engine.on('st.data', (data) => console.log(`[DATA] Sticker ${data.index} gerado em:`, data.file));
|
|
50
|
-
engine.on('st.error', (data) => console.error(`[ERROR] Falha no arquivo ${data.index}:`, data.error));
|
|
51
|
-
|
|
52
|
-
// Adicione arquivos à fila (pode ser URL, Buffer ou caminho local)
|
|
53
|
-
engine.addFile('https://picsum.photos/512') // Exemplo de URL de imagem
|
|
54
|
-
.addFile('caminho/para/seu/video.mp4') // Exemplo de caminho local para vídeo
|
|
55
|
-
.addFile(Buffer.from('...')) // Exemplo de Buffer
|
|
56
|
-
.start()
|
|
57
|
-
.then(results => {
|
|
58
|
-
console.log('Processamento concluído. Resultados:', results);
|
|
59
|
-
})
|
|
60
|
-
.catch(err => {
|
|
61
|
-
console.error('Ocorreu um erro geral no processamento:', err);
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Exemplo de Uso (CommonJS - `index.cjs`)
|
|
66
|
-
|
|
67
|
-
```javascript
|
|
68
|
-
const StickEngine = require('./index.cjs');
|
|
69
|
-
|
|
70
|
-
const engine = new StickEngine({
|
|
71
|
-
metadata: {
|
|
72
|
-
pack: 'Meu Pack CJS',
|
|
73
|
-
author: 'Borutovk7 & Eduh Dev',
|
|
74
|
-
emojis: ['🎉']
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Os eventos e a forma de adicionar arquivos são os mesmos do exemplo ESM.
|
|
79
|
-
engine.on('st.start', (data) => console.log(`[START CJS] Processando arquivo ${data.index}:`, data.input));
|
|
80
|
-
// ... (outros eventos)
|
|
81
|
-
|
|
82
|
-
engine.addFile('https://picsum.photos/200')
|
|
83
|
-
.start()
|
|
84
|
-
.then(results => {
|
|
85
|
-
console.log('Processamento CJS concluído. Resultados:', results);
|
|
86
|
-
})
|
|
87
|
-
.catch(err => {
|
|
88
|
-
console.error('Ocorreu um erro geral no processamento CJS:', err);
|
|
89
|
-
});
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Função `getBuffer`
|
|
93
|
-
|
|
94
|
-
A função `getBuffer` fornecida pelo usuário foi integrada ao módulo para lidar com o download de URLs de forma robusta. Ela pode ser importada e utilizada separadamente, se necessário:
|
|
95
|
-
|
|
96
|
-
```javascript
|
|
97
|
-
// ESM
|
|
98
|
-
import { getBuffer } from './index.js';
|
|
99
|
-
|
|
100
|
-
// CJS
|
|
101
|
-
const { getBuffer } = require('./index.cjs');
|
|
102
|
-
|
|
103
|
-
async function downloadAndProcess() {
|
|
104
|
-
try {
|
|
105
|
-
const buffer = await getBuffer('https://example.com/image.png');
|
|
106
|
-
console.log('Buffer obtido com sucesso:', buffer.length, 'bytes');
|
|
107
|
-
// Faça algo com o buffer
|
|
108
|
-
} catch (error) {
|
|
109
|
-
console.error('Erro ao obter buffer:', error);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
downloadAndProcess();
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Notas Importantes
|
|
116
|
-
|
|
117
|
-
* **Diretrizes `async`**: Lembre-se de que as operações do StickEngine são assíncronas. Certifique-se de usar `async/await` ou `.then().catch()` para lidar com as Promises retornadas, garantindo um código funcional e sem bloqueios.
|
|
118
|
-
* **`extractMetadata` (EXIF) sem `sharp`**: A implementação da manipulação de metadados (EXIF) foi cuidadosamente desenvolvida para **não depender da biblioteca `sharp`**. Esta decisão foi tomada para garantir a máxima compatibilidade e leveza do módulo, especialmente para usuários que executam o código em ambientes com recursos limitados, como o Termux, ou em hosts que não suportam a compilação de dependências nativas como o `sharp`. Agradecemos a compreensão, caro usuário! 👊🏻
|
|
119
|
-
* **Verificação de Atualizações**: Caso encontre algum erro na execução ou comportamento inesperado, verifique se os módulos e dependências foram atualizados. Em alguns casos, pode ser necessário obter a versão mais recente do arquivo.
|
|
120
|
-
|
|
121
|
-
## Créditos
|
|
122
|
-
|
|
123
|
-
Este módulo foi desenvolvido com base nas contribuições e inspiração de:
|
|
124
|
-
|
|
125
|
-
- **Borutovk7** (GitHub: [Borutovk7](https://github.com/Borutovk7))
|
|
126
|
-
- **Eduh Dev**
|
|
127
|
-
- **Manus AI** (Adaptação e refatoração)
|
|
128
|
-
|
|
129
|
-
## Licença
|
|
130
|
-
|
|
131
|
-
Este projeto está licenciado sob a licença ISC. Veja o arquivo `LICENSE` para mais detalhes.
|
package/package.json.bak
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@boruto_vk7/stickengine",
|
|
3
|
-
"version": "0.0.1-alpha",
|
|
4
|
-
"description": "StickEngine module for creating WhatsApp stickers",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/StickEngine.js",
|
|
7
|
-
"module": "dist/StickEngine.js",
|
|
8
|
-
"types": "dist/StickEngine.d.ts",
|
|
9
|
-
"exports": {
|
|
10
|
-
".": {
|
|
11
|
-
"require": "./dist/StickEngine.js",
|
|
12
|
-
"import": "./dist/StickEngine.js",
|
|
13
|
-
"types": "./dist/StickEngine.d.ts"
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"scripts": {
|
|
17
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"sticker",
|
|
21
|
-
"whatsapp",
|
|
22
|
-
"stickengine"
|
|
23
|
-
],
|
|
24
|
-
"author": "Borutovk7 <https://github.com/Borutovk7>",
|
|
25
|
-
"contributors": [
|
|
26
|
-
"Eduh Dev",
|
|
27
|
-
"Manus AI"
|
|
28
|
-
],
|
|
29
|
-
"license": "ISC",
|
|
30
|
-
"repository": {
|
|
31
|
-
"type": "git",
|
|
32
|
-
"url": "https://github.com/Borutovk7/StickEngine.git"
|
|
33
|
-
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"axios": "^1.6.0",
|
|
36
|
-
"file-type": "^16.5.3",
|
|
37
|
-
"fluent-ffmpeg": "^2.1.2",
|
|
38
|
-
"jimp": "^0.22.10",
|
|
39
|
-
"node-fetch": "^2.6.1",
|
|
40
|
-
"node-webpmux": "^1.0.7",
|
|
41
|
-
"remove.bg": "^1.3.0"
|
|
42
|
-
},
|
|
43
|
-
"devDependencies": {
|
|
44
|
-
"@types/fluent-ffmpeg": "^2.1.28",
|
|
45
|
-
"@types/node": "^25.9.2",
|
|
46
|
-
"@types/node-fetch": "2.6.1",
|
|
47
|
-
"typescript": "^6.0.3"
|
|
48
|
-
}
|
|
49
|
-
}
|
package/src/StickEngine.ts
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import axios, { AxiosRequestConfig } from 'axios';
|
|
4
|
-
import { exec } from 'child_process';
|
|
5
|
-
import { EventEmitter } from 'events';
|
|
6
|
-
import ffmpeg from 'fluent-ffmpeg';
|
|
7
|
-
// @ts-ignore
|
|
8
|
-
import webpMux from 'node-webpmux';
|
|
9
|
-
// @ts-ignore
|
|
10
|
-
import Jimp from 'jimp';
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
import { removeBackgroundFromImageFile } from 'remove.bg';
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
14
|
-
|
|
15
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
-
const __dirname = path.dirname(__filename);
|
|
17
|
-
|
|
18
|
-
export interface StickerMetadata {
|
|
19
|
-
pack?: string;
|
|
20
|
-
author?: string;
|
|
21
|
-
emojis?: string[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface StickEngineOptions {
|
|
25
|
-
webp?: boolean;
|
|
26
|
-
edit?: string | boolean;
|
|
27
|
-
convert?: boolean;
|
|
28
|
-
transparent?: string | string[] | false;
|
|
29
|
-
metadata?: StickerMetadata;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Faz GET em uma URL e retorna o corpo como Buffer.
|
|
34
|
-
* Fornecido pelo usuário para integração.
|
|
35
|
-
*/
|
|
36
|
-
export const getBuffer = async (url: string, options: AxiosRequestConfig = {}): Promise<Buffer> => {
|
|
37
|
-
try {
|
|
38
|
-
const { data } = await axios({
|
|
39
|
-
method: 'get',
|
|
40
|
-
url,
|
|
41
|
-
headers: {
|
|
42
|
-
'user-agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36',
|
|
43
|
-
'DNT': '1',
|
|
44
|
-
'Upgrade-Insecure-Request': '1',
|
|
45
|
-
},
|
|
46
|
-
...options,
|
|
47
|
-
responseType: 'arraybuffer',
|
|
48
|
-
});
|
|
49
|
-
return Buffer.from(data);
|
|
50
|
-
} catch (err) {
|
|
51
|
-
console.error(`Erro em getBuffer: ${err}`);
|
|
52
|
-
const errorImagePath = path.join(__dirname, 'src', 'emror.jpg');
|
|
53
|
-
if (fs.existsSync(errorImagePath)) {
|
|
54
|
-
return fs.readFileSync(errorImagePath);
|
|
55
|
-
}
|
|
56
|
-
return Buffer.alloc(0);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export class StickEngine extends EventEmitter {
|
|
61
|
-
public options: StickEngineOptions;
|
|
62
|
-
private tempDir: string;
|
|
63
|
-
private queue: any[];
|
|
64
|
-
|
|
65
|
-
constructor(options: StickEngineOptions = {}) {
|
|
66
|
-
super();
|
|
67
|
-
this.options = {
|
|
68
|
-
webp: true,
|
|
69
|
-
edit: false,
|
|
70
|
-
convert: false,
|
|
71
|
-
transparent: false,
|
|
72
|
-
metadata: {
|
|
73
|
-
pack: 'StickEngine',
|
|
74
|
-
author: 'Borutovk7 & Eduh Dev',
|
|
75
|
-
emojis: ['🥶']
|
|
76
|
-
},
|
|
77
|
-
...options
|
|
78
|
-
};
|
|
79
|
-
this.tempDir = path.join(process.cwd(), 'tmp_stickengine');
|
|
80
|
-
if (!fs.existsSync(this.tempDir)) {
|
|
81
|
-
fs.mkdirSync(this.tempDir, { recursive: true });
|
|
82
|
-
}
|
|
83
|
-
this.queue = [];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
public addFile(file: string | Buffer): this {
|
|
87
|
-
this.queue.push(file);
|
|
88
|
-
return this;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private async processFile(input: string | Buffer, index: number): Promise<string> {
|
|
92
|
-
let filePath: string;
|
|
93
|
-
let extension: string;
|
|
94
|
-
const timestamp = Date.now();
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
this.emit('st.start', { index, input });
|
|
98
|
-
|
|
99
|
-
const ft = await import('file-type');
|
|
100
|
-
const fileTypeFunc = (ft as any).fromBuffer || (ft as any).fileTypeFromBuffer || (ft.default && ((ft.default as any).fromBuffer || (ft.default as any).fileTypeFromBuffer));
|
|
101
|
-
|
|
102
|
-
if (Buffer.isBuffer(input)) {
|
|
103
|
-
const type = await fileTypeFunc(input);
|
|
104
|
-
extension = type ? type.ext : 'bin';
|
|
105
|
-
filePath = path.join(this.tempDir, `input_${timestamp}_${index}.${extension}`);
|
|
106
|
-
fs.writeFileSync(filePath, input);
|
|
107
|
-
} else if (typeof input === 'string' && input.startsWith('http')) {
|
|
108
|
-
const buffer = await getBuffer(input);
|
|
109
|
-
const type = await fileTypeFunc(buffer);
|
|
110
|
-
extension = type ? type.ext : 'bin';
|
|
111
|
-
filePath = path.join(this.tempDir, `input_${timestamp}_${index}.${extension}`);
|
|
112
|
-
fs.writeFileSync(filePath, buffer);
|
|
113
|
-
} else if (typeof input === 'string' && fs.existsSync(input)) {
|
|
114
|
-
extension = path.extname(input).slice(1);
|
|
115
|
-
filePath = input;
|
|
116
|
-
} else {
|
|
117
|
-
throw new Error('Tipo de entrada inválido ou arquivo não encontrado.');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const metadata = await this.getMediaInfo(filePath);
|
|
121
|
-
this.emit('st.info', { index, ...metadata });
|
|
122
|
-
|
|
123
|
-
let currentFile = filePath;
|
|
124
|
-
|
|
125
|
-
if (this.options.transparent) {
|
|
126
|
-
const apiKey = Array.isArray(this.options.transparent)
|
|
127
|
-
? this.options.transparent[Math.floor(Math.random() * this.options.transparent.length)]
|
|
128
|
-
: this.options.transparent;
|
|
129
|
-
|
|
130
|
-
if (typeof apiKey === 'string') {
|
|
131
|
-
const bgRemovedPath = path.join(this.tempDir, `nobg_${timestamp}_${index}.png`);
|
|
132
|
-
const result = await removeBackgroundFromImageFile({
|
|
133
|
-
path: currentFile,
|
|
134
|
-
apiKey,
|
|
135
|
-
size: 'auto',
|
|
136
|
-
type: 'auto'
|
|
137
|
-
});
|
|
138
|
-
fs.writeFileSync(bgRemovedPath, Buffer.from(result.base64img, 'base64'));
|
|
139
|
-
currentFile = bgRemovedPath;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const webpPath = path.join(this.tempDir, `sticker_${timestamp}_${index}.webp`);
|
|
144
|
-
await this.convertToWebp(currentFile, webpPath);
|
|
145
|
-
currentFile = webpPath;
|
|
146
|
-
|
|
147
|
-
if (this.options.metadata) {
|
|
148
|
-
await this.addExif(currentFile, this.options.metadata);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
this.emit('st.data', { index, file: currentFile });
|
|
152
|
-
return currentFile;
|
|
153
|
-
|
|
154
|
-
} catch (error: any) {
|
|
155
|
-
this.emit('st.error', { index, error: error.message });
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private getMediaInfo(file: string): Promise<any> {
|
|
161
|
-
return new Promise((resolve, reject) => {
|
|
162
|
-
exec(`ffprobe -v error -select_streams v:0 -show_entries stream=width,height,duration -of csv=p=0:s=x ${file}`, (err, stdout) => {
|
|
163
|
-
if (err) return reject(err);
|
|
164
|
-
const [width, height, duration] = stdout.split('x');
|
|
165
|
-
resolve({
|
|
166
|
-
width: parseInt(width) || 0,
|
|
167
|
-
height: parseInt(height) || 0,
|
|
168
|
-
duration: parseFloat(duration) || 0
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
private convertToWebp(input: string, output: string): Promise<void> {
|
|
175
|
-
return new Promise((resolve, reject) => {
|
|
176
|
-
ffmpeg(input)
|
|
177
|
-
.on('error', reject)
|
|
178
|
-
.on('end', () => resolve())
|
|
179
|
-
.addOutputOptions([
|
|
180
|
-
'-vcodec', 'libwebp',
|
|
181
|
-
'-vf', 'scale=512:512:force_original_aspect_ratio=decrease,fps=15,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=#00000000,setsar=1',
|
|
182
|
-
'-lossless', '1',
|
|
183
|
-
'-loop', '0',
|
|
184
|
-
'-preset', 'default',
|
|
185
|
-
'-an',
|
|
186
|
-
'-vsync', '0'
|
|
187
|
-
])
|
|
188
|
-
.toFormat('webp')
|
|
189
|
-
.save(output);
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
private async addExif(webpPath: string, metadata: StickerMetadata): Promise<void> {
|
|
194
|
-
const json = {
|
|
195
|
-
'sticker-pack-id': 'StickEngine-Manus',
|
|
196
|
-
'sticker-pack-name': metadata.pack || 'StickEngine',
|
|
197
|
-
'sticker-pack-publisher': metadata.author || 'Borutovk7 & Eduh Dev',
|
|
198
|
-
'emojis': metadata.emojis || ['🥶']
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
const Image = webpMux.Image || (webpMux.default && webpMux.default.Image);
|
|
202
|
-
const img = new Image();
|
|
203
|
-
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]);
|
|
204
|
-
const jsonBuff = Buffer.from(JSON.stringify(json), 'utf-8');
|
|
205
|
-
const exifBuffer = Buffer.concat([exifAttr, jsonBuff]);
|
|
206
|
-
exifBuffer.writeUIntLE(jsonBuff.length, 14, 4);
|
|
207
|
-
|
|
208
|
-
await img.load(webpPath);
|
|
209
|
-
img.exif = exifBuffer;
|
|
210
|
-
await img.save(webpPath);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
public async start(): Promise<PromiseSettledResult<string>[]> {
|
|
214
|
-
if (this.queue.length === 0) throw new Error('Nenhum arquivo na fila.');
|
|
215
|
-
const results = await Promise.allSettled(this.queue.map((file, i) => this.processFile(file, i)));
|
|
216
|
-
this.queue = [];
|
|
217
|
-
return results;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export default StickEngine;
|
package/src/emror.jpg
DELETED
|
File without changes
|
package/tsconfig.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"declaration": true,
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"allowJs": true,
|
|
14
|
-
"resolveJsonModule": true
|
|
15
|
-
},
|
|
16
|
-
"include": ["src/**/*"],
|
|
17
|
-
"exclude": ["node_modules", "dist"]
|
|
18
|
-
}
|