@boruto_vk7/stickengine 0.0.4 → 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/dist/StickEngine.js +64 -60
- package/package.json +1 -1
package/dist/StickEngine.js
CHANGED
|
@@ -3,19 +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
|
|
10
7
|
import Jimp from 'jimp';
|
|
11
|
-
// @ts-ignore
|
|
12
8
|
import { removeBackgroundFromImageFile } from 'remove.bg';
|
|
13
9
|
import { fileURLToPath } from 'url';
|
|
10
|
+
|
|
14
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
12
|
const __dirname = path.dirname(__filename);
|
|
16
|
-
|
|
17
|
-
* Faz GET em uma URL e retorna o corpo como Buffer.
|
|
18
|
-
*/
|
|
13
|
+
|
|
19
14
|
export const getBuffer = async (url, options = {}) => {
|
|
20
15
|
try {
|
|
21
16
|
const { data } = await axios({
|
|
@@ -40,11 +35,13 @@ export const getBuffer = async (url, options = {}) => {
|
|
|
40
35
|
return Buffer.alloc(0);
|
|
41
36
|
}
|
|
42
37
|
};
|
|
38
|
+
|
|
43
39
|
export class StickEngine extends EventEmitter {
|
|
44
40
|
options;
|
|
45
41
|
tempDir;
|
|
46
42
|
queue;
|
|
47
43
|
createdFiles;
|
|
44
|
+
|
|
48
45
|
constructor(options = {}) {
|
|
49
46
|
super();
|
|
50
47
|
this.options = {
|
|
@@ -69,10 +66,12 @@ export class StickEngine extends EventEmitter {
|
|
|
69
66
|
this.queue = [];
|
|
70
67
|
this.createdFiles = [];
|
|
71
68
|
}
|
|
69
|
+
|
|
72
70
|
addFile(file) {
|
|
73
71
|
this.queue.push(file);
|
|
74
72
|
return this;
|
|
75
73
|
}
|
|
74
|
+
|
|
76
75
|
clean() {
|
|
77
76
|
for (const file of this.createdFiles) {
|
|
78
77
|
try {
|
|
@@ -86,6 +85,7 @@ export class StickEngine extends EventEmitter {
|
|
|
86
85
|
}
|
|
87
86
|
this.createdFiles = [];
|
|
88
87
|
}
|
|
88
|
+
|
|
89
89
|
async processFile(input, index) {
|
|
90
90
|
let filePath = '';
|
|
91
91
|
let extension = '';
|
|
@@ -95,6 +95,7 @@ export class StickEngine extends EventEmitter {
|
|
|
95
95
|
this.emit('st.start', { index, input });
|
|
96
96
|
const ft = await import('file-type');
|
|
97
97
|
const fileTypeFunc = ft.fromBuffer || ft.fileTypeFromBuffer || (ft.default && (ft.default.fromBuffer || ft.default.fileTypeFromBuffer));
|
|
98
|
+
|
|
98
99
|
if (Buffer.isBuffer(input)) {
|
|
99
100
|
const type = await fileTypeFunc(input);
|
|
100
101
|
extension = type ? type.ext : 'bin';
|
|
@@ -116,18 +117,19 @@ export class StickEngine extends EventEmitter {
|
|
|
116
117
|
else {
|
|
117
118
|
throw new Error('Tipo de entrada inválido ou arquivo não encontrado.');
|
|
118
119
|
}
|
|
120
|
+
|
|
119
121
|
const mediaInfo = await this.getMediaInfo(filePath);
|
|
120
122
|
this.emit('st.info', { index, ...mediaInfo });
|
|
121
123
|
let currentFile = filePath;
|
|
122
124
|
const isAnimated = mediaInfo.duration > 0 || extension === 'gif' || extension === 'mp4' || extension === 'webp';
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
if (this.options.edit && !isAnimated) {
|
|
125
127
|
const editedPath = path.join(this.tempDir, `edited_${timestamp}_${index}.png`);
|
|
126
128
|
await this.applyJimpEdits(currentFile, editedPath, this.options.edit);
|
|
127
129
|
localCreatedFiles.push(editedPath);
|
|
128
130
|
currentFile = editedPath;
|
|
129
131
|
}
|
|
130
|
-
|
|
132
|
+
|
|
131
133
|
if (this.options.transparent && !isAnimated) {
|
|
132
134
|
const apiKey = Array.isArray(this.options.transparent)
|
|
133
135
|
? this.options.transparent[Math.floor(Math.random() * this.options.transparent.length)]
|
|
@@ -145,15 +147,16 @@ export class StickEngine extends EventEmitter {
|
|
|
145
147
|
currentFile = bgRemovedPath;
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
|
-
|
|
150
|
+
|
|
149
151
|
const webpPath = path.join(this.tempDir, `sticker_${timestamp}_${index}.webp`);
|
|
150
152
|
await this.convertToWebp(currentFile, webpPath, isAnimated);
|
|
151
153
|
localCreatedFiles.push(webpPath);
|
|
152
154
|
currentFile = webpPath;
|
|
153
|
-
|
|
155
|
+
|
|
154
156
|
if (this.options.metadata) {
|
|
155
157
|
await this.addExif(currentFile, this.options.metadata);
|
|
156
158
|
}
|
|
159
|
+
|
|
157
160
|
this.createdFiles.push(...localCreatedFiles);
|
|
158
161
|
this.emit('st.data', { index, file: currentFile });
|
|
159
162
|
return currentFile;
|
|
@@ -167,18 +170,14 @@ export class StickEngine extends EventEmitter {
|
|
|
167
170
|
throw error;
|
|
168
171
|
}
|
|
169
172
|
}
|
|
173
|
+
|
|
170
174
|
async applyJimpEdits(input, output, options) {
|
|
171
175
|
const image = await Jimp.read(input);
|
|
172
|
-
if (options.greyscale)
|
|
173
|
-
|
|
174
|
-
if (options.
|
|
175
|
-
|
|
176
|
-
if (options.
|
|
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);
|
|
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);
|
|
182
181
|
if (options.text) {
|
|
183
182
|
const font = await Jimp.loadFont(Jimp.FONT_SANS_32_WHITE);
|
|
184
183
|
image.print(font, 0, 0, {
|
|
@@ -189,11 +188,11 @@ export class StickEngine extends EventEmitter {
|
|
|
189
188
|
}
|
|
190
189
|
await image.writeAsync(output);
|
|
191
190
|
}
|
|
191
|
+
|
|
192
192
|
getMediaInfo(file) {
|
|
193
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)
|
|
196
|
-
return resolve({ width: 0, height: 0, duration: 0 });
|
|
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 });
|
|
197
196
|
const parts = stdout.trim().split('x');
|
|
198
197
|
resolve({
|
|
199
198
|
width: parseInt(parts[0]) || 0,
|
|
@@ -203,53 +202,58 @@ export class StickEngine extends EventEmitter {
|
|
|
203
202
|
});
|
|
204
203
|
});
|
|
205
204
|
}
|
|
205
|
+
|
|
206
206
|
convertToWebp(input, output, isAnimated) {
|
|
207
207
|
return new Promise((resolve, reject) => {
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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`,
|
|
214
|
-
'-lossless', '1',
|
|
215
|
-
'-loop', '0',
|
|
216
|
-
'-preset', 'default',
|
|
217
|
-
'-an',
|
|
218
|
-
'-vsync', '0'
|
|
219
|
-
];
|
|
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;
|
|
220
213
|
if (isAnimated) {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
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}"`;
|
|
224
217
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
218
|
+
|
|
219
|
+
exec(cmd, (err, stdout, stderr) => {
|
|
220
|
+
if (err) return reject(new Error(stderr || err.message));
|
|
221
|
+
resolve();
|
|
222
|
+
});
|
|
228
223
|
});
|
|
229
224
|
}
|
|
225
|
+
|
|
230
226
|
async addExif(webpPath, metadata) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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);
|
|
246
248
|
}
|
|
249
|
+
}
|
|
250
|
+
|
|
247
251
|
async start() {
|
|
248
|
-
if (this.queue.length === 0)
|
|
249
|
-
throw new Error('Nenhum arquivo na fila.');
|
|
252
|
+
if (this.queue.length === 0) throw new Error('Nenhum arquivo na fila.');
|
|
250
253
|
const results = await Promise.allSettled(this.queue.map((file, i) => this.processFile(file, i)));
|
|
251
254
|
this.queue = [];
|
|
252
255
|
return results;
|
|
253
256
|
}
|
|
254
257
|
}
|
|
255
|
-
|
|
258
|
+
|
|
259
|
+
export default StickEngine;
|