apexify.js 4.5.30 → 4.5.40

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.
Files changed (76) hide show
  1. package/dist/ai/ApexAI.d.ts.map +1 -1
  2. package/dist/ai/ApexAI.js +5 -12
  3. package/dist/ai/ApexAI.js.map +1 -1
  4. package/dist/ai/ApexModules.d.ts.map +1 -1
  5. package/dist/ai/ApexModules.js +26 -29
  6. package/dist/ai/ApexModules.js.map +1 -1
  7. package/dist/ai/functions/draw.d.ts.map +1 -1
  8. package/dist/ai/functions/draw.js +2 -10
  9. package/dist/ai/functions/draw.js.map +1 -1
  10. package/dist/ai/functions/validOptions.d.ts +6 -12
  11. package/dist/ai/functions/validOptions.d.ts.map +1 -1
  12. package/dist/ai/functions/validOptions.js +54 -32
  13. package/dist/ai/functions/validOptions.js.map +1 -1
  14. package/dist/ai/modals-chat/electronHub/chatmodels.d.ts +2 -2
  15. package/dist/ai/modals-chat/electronHub/chatmodels.d.ts.map +1 -1
  16. package/dist/ai/modals-chat/electronHub/chatmodels.js +26 -29
  17. package/dist/ai/modals-chat/electronHub/chatmodels.js.map +1 -1
  18. package/dist/ai/modals-chat/electronHub/imageModels.d.ts +1 -1
  19. package/dist/ai/modals-chat/electronHub/imageModels.d.ts.map +1 -1
  20. package/dist/ai/modals-chat/electronHub/imageModels.js +29 -24
  21. package/dist/ai/modals-chat/electronHub/imageModels.js.map +1 -1
  22. package/dist/ai/modals-chat/groq/chatgroq.d.ts.map +1 -1
  23. package/dist/ai/modals-chat/groq/chatgroq.js +10 -4
  24. package/dist/ai/modals-chat/groq/chatgroq.js.map +1 -1
  25. package/dist/ai/modals-chat/groq/imageAnalyzer.d.ts.map +1 -1
  26. package/dist/ai/modals-chat/groq/imageAnalyzer.js +0 -1
  27. package/dist/ai/modals-chat/groq/imageAnalyzer.js.map +1 -1
  28. package/dist/ai/modals-chat/groq/whisper.d.ts.map +1 -1
  29. package/dist/ai/modals-chat/groq/whisper.js +0 -3
  30. package/dist/ai/modals-chat/groq/whisper.js.map +1 -1
  31. package/dist/canvas/ApexPainter.d.ts +94 -7
  32. package/dist/canvas/ApexPainter.d.ts.map +1 -1
  33. package/dist/canvas/ApexPainter.js +443 -119
  34. package/dist/canvas/ApexPainter.js.map +1 -1
  35. package/dist/canvas/utils/bg.d.ts +3 -3
  36. package/dist/canvas/utils/bg.d.ts.map +1 -1
  37. package/dist/canvas/utils/bg.js +35 -15
  38. package/dist/canvas/utils/bg.js.map +1 -1
  39. package/dist/canvas/utils/customLines.d.ts +2 -1
  40. package/dist/canvas/utils/customLines.d.ts.map +1 -1
  41. package/dist/canvas/utils/customLines.js +67 -31
  42. package/dist/canvas/utils/customLines.js.map +1 -1
  43. package/dist/canvas/utils/general functions.d.ts +2 -2
  44. package/dist/canvas/utils/general functions.d.ts.map +1 -1
  45. package/dist/canvas/utils/general functions.js +52 -15
  46. package/dist/canvas/utils/general functions.js.map +1 -1
  47. package/dist/canvas/utils/types.d.ts +68 -0
  48. package/dist/canvas/utils/types.d.ts.map +1 -1
  49. package/dist/canvas/utils/types.js +2 -4
  50. package/dist/canvas/utils/types.js.map +1 -1
  51. package/dist/canvas/utils/utils.d.ts +2 -2
  52. package/dist/canvas/utils/utils.d.ts.map +1 -1
  53. package/dist/index.d.ts +4 -7
  54. package/dist/index.d.ts.map +1 -1
  55. package/examples/barchart.txt +71 -0
  56. package/examples/linechart.txt +93 -0
  57. package/examples/piechart.txt +67 -0
  58. package/lib/ai/ApexAI.ts +8 -11
  59. package/lib/ai/ApexModules.ts +42 -33
  60. package/lib/ai/functions/draw.ts +2 -8
  61. package/lib/ai/functions/validOptions.ts +83 -58
  62. package/lib/ai/modals-chat/electronHub/chatmodels.ts +40 -43
  63. package/lib/ai/modals-chat/electronHub/imageModels.ts +34 -32
  64. package/lib/ai/modals-chat/groq/chatgroq.ts +17 -7
  65. package/lib/ai/modals-chat/groq/imageAnalyzer.ts +0 -2
  66. package/lib/ai/modals-chat/groq/whisper.ts +0 -3
  67. package/lib/canvas/ApexPainter.ts +665 -262
  68. package/lib/canvas/utils/bg.ts +42 -16
  69. package/lib/canvas/utils/customLines.ts +88 -43
  70. package/lib/canvas/utils/general functions.ts +98 -41
  71. package/lib/canvas/utils/types.ts +77 -1
  72. package/lib/canvas/utils/utils.ts +6 -2
  73. package/package.json +24 -9
  74. package/lib/ai/modals-chat/freesedgpt/chat.ts +0 -31
  75. package/lib/ai/modals-chat/freesedgpt/fresedImagine.ts +0 -24
  76. package/lib/ai/modals-chat/rsn/rsnChat.ts +0 -74
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ApexPainter = void 0;
7
7
  const canvas_1 = require("@napi-rs/canvas");
8
8
  const gifencoder_1 = __importDefault(require("gifencoder"));
9
+ const fluent_ffmpeg_1 = __importDefault(require("fluent-ffmpeg"));
9
10
  const stream_1 = require("stream");
11
+ const axios_1 = __importDefault(require("axios"));
10
12
  const fs_1 = __importDefault(require("fs"));
11
13
  const path_1 = __importDefault(require("path"));
12
14
  const utils_1 = require("./utils/utils");
@@ -48,12 +50,18 @@ class ApexPainter {
48
50
  if (!Array.isArray(images)) {
49
51
  images = [images];
50
52
  }
51
- if (!canvasBuffer.buffer) {
52
- throw new Error('You need to provide your canvasConfig. Check documentation if you don\'t know.');
53
+ let existingCanvas;
54
+ if (Buffer.isBuffer(canvasBuffer)) {
55
+ existingCanvas = await (0, canvas_1.loadImage)(canvasBuffer);
56
+ }
57
+ else if (canvasBuffer && canvasBuffer.buffer) {
58
+ existingCanvas = await (0, canvas_1.loadImage)(canvasBuffer.buffer);
59
+ }
60
+ else {
61
+ throw new Error('Invalid canvasBuffer provided, should be Buffer or CanvasResults object with buffer');
53
62
  }
54
- const existingCanvas = await (0, canvas_1.loadImage)(canvasBuffer.buffer);
55
63
  if (!existingCanvas) {
56
- throw new Error('Either buffer or canvasConfig is an empty background with no properties');
64
+ throw new Error('Unable to load image from buffer');
57
65
  }
58
66
  const canvas = (0, canvas_1.createCanvas)(existingCanvas.width, existingCanvas.height);
59
67
  const ctx = canvas.getContext('2d');
@@ -68,9 +76,22 @@ class ApexPainter {
68
76
  }
69
77
  async createText(textOptionsArray, buffer) {
70
78
  try {
71
- const existingImage = await (0, canvas_1.loadImage)(buffer.buffer);
72
- if (!existingImage)
73
- throw new Error("Invalid image buffer. Make sure to pass a valid canvas buffer.");
79
+ if (!Array.isArray(textOptionsArray)) {
80
+ textOptionsArray = [textOptionsArray];
81
+ }
82
+ let existingImage;
83
+ if (Buffer.isBuffer(buffer)) {
84
+ existingImage = await (0, canvas_1.loadImage)(buffer);
85
+ }
86
+ else if (buffer && buffer.buffer) {
87
+ existingImage = await (0, canvas_1.loadImage)(buffer.buffer);
88
+ }
89
+ else {
90
+ throw new Error('Invalid canvasBuffer provided. It should be a Buffer or CanvasResults object with a buffer');
91
+ }
92
+ if (!existingImage) {
93
+ throw new Error('Unable to load image from buffer');
94
+ }
74
95
  const canvas = (0, canvas_1.createCanvas)(existingImage.width, existingImage.height);
75
96
  const ctx = canvas.getContext("2d");
76
97
  ctx.drawImage(existingImage, 0, 0);
@@ -89,11 +110,24 @@ class ApexPainter {
89
110
  throw new Error("Invalid image buffer");
90
111
  }
91
112
  }
92
- async createCustom(buffer, options) {
113
+ async createCustom(options, buffer) {
93
114
  try {
94
- const existingImage = await (0, canvas_1.loadImage)(buffer.buffer);
95
- if (!existingImage)
96
- throw new Error("Invalid image buffer. Make sure to pass a valid canvas buffer.");
115
+ if (!Array.isArray(options)) {
116
+ options = [options];
117
+ }
118
+ let existingImage;
119
+ if (Buffer.isBuffer(buffer)) {
120
+ existingImage = await (0, canvas_1.loadImage)(buffer);
121
+ }
122
+ else if (buffer && buffer.buffer) {
123
+ existingImage = await (0, canvas_1.loadImage)(buffer.buffer);
124
+ }
125
+ else {
126
+ throw new Error('Invalid canvasBuffer provided. It should be a Buffer or CanvasResults object with a buffer');
127
+ }
128
+ if (!existingImage) {
129
+ throw new Error('Unable to load image from buffer');
130
+ }
97
131
  const canvas = (0, canvas_1.createCanvas)(existingImage.width, existingImage.height);
98
132
  const ctx = canvas.getContext("2d");
99
133
  ctx.drawImage(existingImage, 0, 0);
@@ -105,7 +139,7 @@ class ApexPainter {
105
139
  throw new Error("Failed to create custom image");
106
140
  }
107
141
  }
108
- async createGIF(images, options) {
142
+ async createGIF(gifFrames, options) {
109
143
  async function resizeImage(image, targetWidth, targetHeight) {
110
144
  const canvas = (0, canvas_1.createCanvas)(targetWidth, targetHeight);
111
145
  const ctx = canvas.getContext("2d");
@@ -121,176 +155,94 @@ class ApexPainter {
121
155
  bufferStream.on('data', (chunk) => {
122
156
  chunks.push(chunk);
123
157
  });
124
- const customBufferStream = bufferStream;
125
- customBufferStream.getBuffer = function () {
126
- return Buffer.concat(chunks);
158
+ return {
159
+ ...bufferStream,
160
+ getBuffer: function () {
161
+ return Buffer.concat(chunks);
162
+ }
127
163
  };
128
- return customBufferStream;
129
164
  }
130
165
  function validateOptions(options) {
131
166
  if (options.outputFormat === "file" && !options.outputFile) {
132
167
  throw new Error("Output file path is required when using file output format.");
133
168
  }
134
- if (options.repeat !== undefined &&
135
- (typeof options.repeat !== "number" || options.repeat < 0)) {
169
+ if (options.repeat !== undefined && (typeof options.repeat !== "number" || options.repeat < 0)) {
136
170
  throw new Error("Repeat must be a non-negative number or undefined.");
137
171
  }
138
- if (options.quality !== undefined &&
139
- (typeof options.quality !== "number" ||
140
- options.quality < 1 ||
141
- options.quality > 20)) {
172
+ if (options.quality !== undefined && (typeof options.quality !== "number" || options.quality < 1 || options.quality > 20)) {
142
173
  throw new Error("Quality must be a number between 1 and 20 or undefined.");
143
174
  }
144
- if (options.canvasSize) {
145
- if (options.canvasSize.width !== undefined &&
146
- (!Number.isInteger(options.canvasSize.width) ||
147
- options.canvasSize.width <= 0)) {
148
- throw new Error("Canvas width must be a positive integer or undefined.");
149
- }
150
- if (options.canvasSize.height !== undefined &&
151
- (!Number.isInteger(options.canvasSize.height) ||
152
- options.canvasSize.height <= 0)) {
153
- throw new Error("Canvas height must be a positive integer or undefined.");
154
- }
155
- }
156
- if (options.delay !== undefined &&
157
- (!Number.isInteger(options.delay) || options.delay <= 0)) {
158
- throw new Error("Delay must be a positive integer or undefined.");
159
- }
160
- if (options.watermark !== undefined &&
161
- typeof options.watermark !== "boolean") {
175
+ if (options.watermark && typeof options.watermark.enable !== "boolean") {
162
176
  throw new Error("Watermark must be a boolean or undefined.");
163
177
  }
164
- if (options.textOverlay !== undefined) {
165
- if (!options.textOverlay.text ||
166
- typeof options.textOverlay.text !== "string") {
178
+ if (options.textOverlay) {
179
+ const textOptions = options.textOverlay;
180
+ if (!textOptions.text || typeof textOptions.text !== "string") {
167
181
  throw new Error("Text overlay text is required and must be a string.");
168
182
  }
169
- if (options.textOverlay.fontName !== undefined &&
170
- typeof options.textOverlay.fontName !== "string") {
171
- throw new Error("Text overlay fontName must be a string or undefined.");
172
- }
173
- if (options.textOverlay.fontPath !== undefined &&
174
- typeof options.textOverlay.fontPath !== "string") {
175
- throw new Error("Text overlay fontPath must be a string or undefined.");
176
- }
177
- if (options.textOverlay.fontSize !== undefined &&
178
- (!Number.isInteger(options.textOverlay.fontSize) ||
179
- options.textOverlay.fontSize <= 0)) {
183
+ if (textOptions.fontSize !== undefined && (!Number.isInteger(textOptions.fontSize) || textOptions.fontSize <= 0)) {
180
184
  throw new Error("Text overlay fontSize must be a positive integer or undefined.");
181
185
  }
182
- if (options.textOverlay.fontColor !== undefined &&
183
- typeof options.textOverlay.fontColor !== "string") {
186
+ if (textOptions.fontColor !== undefined && typeof textOptions.fontColor !== "string") {
184
187
  throw new Error("Text overlay fontColor must be a string or undefined.");
185
188
  }
186
189
  }
187
190
  }
188
- function validateImageObject(imageObject) {
189
- return (imageObject &&
190
- typeof imageObject === "object" &&
191
- "source" in imageObject &&
192
- "isRemote" in imageObject);
193
- }
194
- function validateImages(images) {
195
- if (!Array.isArray(images)) {
196
- throw new Error('The "images" parameter must be an array.');
197
- }
198
- if (images.length === 0) {
199
- throw new Error('The "images" array must contain at least one image object.');
200
- }
201
- for (const imageObject of images) {
202
- if (!validateImageObject(imageObject)) {
203
- throw new Error('Each image object must have "source" and "isRemote" properties.');
204
- }
205
- }
206
- }
207
191
  try {
208
192
  validateOptions(options);
209
- validateImages(images);
210
193
  const canvasWidth = options.width || 1200;
211
194
  const canvasHeight = options.height || 1200;
212
195
  const encoder = new gifencoder_1.default(canvasWidth, canvasHeight);
213
- const outputStream = options.outputFile
214
- ? createOutputStream(options.outputFile)
215
- : createBufferStream();
216
- // @ts-ignore: Ignore type checking for this line
196
+ const outputStream = options.outputFile ? createOutputStream(options.outputFile) : createBufferStream();
217
197
  encoder.createReadStream().pipe(outputStream);
218
198
  encoder.start();
219
199
  encoder.setRepeat(options.repeat || 0);
220
200
  encoder.setQuality(options.quality || 10);
221
- encoder.setDelay(options.delay || 3000);
222
201
  const canvas = (0, canvas_1.createCanvas)(canvasWidth, canvasHeight);
223
202
  const ctx = canvas.getContext("2d");
224
- for (const imageInfo of images) {
225
- const image = imageInfo.isRemote
226
- ? await (0, canvas_1.loadImage)(imageInfo.source)
227
- : await (0, canvas_1.loadImage)(imageInfo.source);
203
+ for (const frame of gifFrames) {
204
+ const image = await (0, canvas_1.loadImage)(frame.background);
228
205
  const resizedImage = await resizeImage(image, canvasWidth, canvasHeight);
229
206
  ctx.clearRect(0, 0, canvas.width, canvas.height);
230
207
  ctx.drawImage(resizedImage, 0, 0);
231
208
  if (options.watermark?.enable) {
232
- const watermark = await (0, canvas_1.loadImage)(options.watermark?.url);
233
- if (!watermark)
234
- throw new Error("Invalid watermark url");
209
+ const watermark = await (0, canvas_1.loadImage)(options.watermark.url);
235
210
  ctx.drawImage(watermark, 10, canvasHeight - watermark.height - 10);
236
211
  }
237
212
  if (options.textOverlay) {
238
- const textOptions = options.textOverlay;
239
- const fontPath = textOptions.fontPath;
240
- const fontName = textOptions.fontName || "Arial";
241
- const fontSize = textOptions.fontSize || 20;
242
- const fontColor = textOptions.fontColor || "white";
243
- const x = textOptions.x || 10;
244
- const y = textOptions.y || 30;
245
- if (fontPath) {
246
- canvas_1.GlobalFonts.registerFromPath(path_1.default.join(options.basDir, fontPath), fontName);
247
- }
248
- ctx.font = `${fontSize}px ${fontName}`;
249
- ctx.fillStyle = fontColor;
250
- ctx.fillText(textOptions.text, x, y);
213
+ ctx.font = `${options.textOverlay.fontSize || 20}px Arial`;
214
+ ctx.fillStyle = options.textOverlay.fontColor || "white";
215
+ ctx.fillText(options.textOverlay.text, options.textOverlay.x || 10, options.textOverlay.y || 30);
251
216
  }
217
+ encoder.setDelay(frame.duration);
252
218
  encoder.addFrame(ctx);
253
219
  }
254
220
  encoder.finish();
255
221
  outputStream.end();
256
222
  if (options.outputFormat === "file") {
257
- if (!options.outputFile) {
258
- throw new Error("Please provide a valid file path");
259
- }
260
223
  await new Promise((resolve) => outputStream.on("finish", resolve));
261
224
  }
262
225
  else if (options.outputFormat === "base64") {
263
- outputStream.on("finish", () => {
264
- });
265
226
  if ('getBuffer' in outputStream) {
266
227
  return outputStream.getBuffer().toString("base64");
267
228
  }
268
- else {
269
- throw new Error("outputStream does not have getBuffer method");
270
- }
271
229
  }
272
230
  else if (options.outputFormat === "attachment") {
273
- outputStream.on("finish", () => {
274
- });
275
231
  const gifStream = encoder.createReadStream();
276
232
  return [{ attachment: gifStream, name: "gif.js" }];
277
233
  }
278
234
  else if (options.outputFormat === "buffer") {
279
- outputStream.on("finish", () => {
280
- });
281
235
  if ('getBuffer' in outputStream) {
282
236
  return outputStream.getBuffer();
283
237
  }
284
- else {
285
- throw new Error("outputStream does not have getBuffer method");
286
- }
287
238
  }
288
239
  else {
289
- throw new Error("Error: Please provide a valid format: 'buffer', 'base64', 'attachment', or 'output/file/path'.");
240
+ throw new Error("Invalid output format. Supported formats are 'file', 'base64', 'attachment', and 'buffer'.");
290
241
  }
291
242
  }
292
243
  catch (e) {
293
244
  console.error(e.message);
245
+ throw e; // Re-throw the error after logging
294
246
  }
295
247
  }
296
248
  async resize(resizeOptions) {
@@ -302,8 +254,8 @@ class ApexPainter {
302
254
  async processImage(imagePath, filters) {
303
255
  return (0, utils_1.imgEffects)(imagePath, filters);
304
256
  }
305
- async colorsFilter(imagePath, filterColor) {
306
- return (0, utils_1.applyColorFilters)(imagePath, filterColor);
257
+ async colorsFilter(imagePath, filterColor, opacity) {
258
+ return (0, utils_1.applyColorFilters)(imagePath, filterColor, opacity);
307
259
  }
308
260
  async colorAnalysis(imagePath) {
309
261
  return (0, utils_1.detectColors)(imagePath);
@@ -314,6 +266,30 @@ class ApexPainter {
314
266
  async removeBackground(imageURL, apiKey) {
315
267
  return (0, utils_1.bgRemoval)(imageURL, apiKey);
316
268
  }
269
+ async blend(layers, baseImageBuffer) {
270
+ try {
271
+ // Load the base image
272
+ const baseImage = await (0, canvas_1.loadImage)(baseImageBuffer);
273
+ const canvas = (0, canvas_1.createCanvas)(baseImage.width, baseImage.height);
274
+ const ctx = canvas.getContext('2d');
275
+ // Draw the base image
276
+ ctx.drawImage(baseImage, 0, 0);
277
+ // Process each layer
278
+ for (const layer of layers) {
279
+ const layerImage = await (0, canvas_1.loadImage)(layer.image);
280
+ ctx.globalAlpha = layer.opacity !== undefined ? layer.opacity : 1.0;
281
+ ctx.globalCompositeOperation = layer.blendMode;
282
+ ctx.drawImage(layerImage, layer.position?.x || 0, layer.position?.y || 0);
283
+ }
284
+ ctx.globalAlpha = 1.0;
285
+ ctx.globalCompositeOperation = 'source-over';
286
+ return canvas.toBuffer('image/png');
287
+ }
288
+ catch (error) {
289
+ console.error('Error creating layered composition:', error);
290
+ throw new Error('Failed to create layered composition');
291
+ }
292
+ }
317
293
  async createChart(data, type) {
318
294
  if (!data || Object.keys(data).length === 0) {
319
295
  throw new Error('You need to provide datasets to create charts.');
@@ -421,6 +397,354 @@ class ApexPainter {
421
397
  ctx.globalAlpha = 1.0;
422
398
  }
423
399
  }
400
+ async extractFrames(videoSource, options) {
401
+ const frames = [];
402
+ const frameDir = path_1.default.join(__dirname, 'frames');
403
+ if (!fs_1.default.existsSync(frameDir)) {
404
+ fs_1.default.mkdirSync(frameDir);
405
+ }
406
+ const videoPath = typeof videoSource === 'string' ? videoSource : path_1.default.join(frameDir, 'temp-video.mp4');
407
+ if (Buffer.isBuffer(videoSource)) {
408
+ fs_1.default.writeFileSync(videoPath, videoSource);
409
+ }
410
+ else if (videoSource.startsWith('http')) {
411
+ await (0, axios_1.default)({
412
+ method: 'get',
413
+ url: videoSource,
414
+ responseType: 'arraybuffer'
415
+ })
416
+ .then((response) => {
417
+ fs_1.default.writeFileSync(videoPath, response.data);
418
+ })
419
+ .catch(err => {
420
+ throw new Error(`Error downloading video: ${err.message}`);
421
+ });
422
+ }
423
+ else if (!fs_1.default.existsSync(videoPath)) {
424
+ throw new Error("Video file not found at specified path.");
425
+ }
426
+ function processVideoExtraction(videoPath, frames, options, resolve, reject) {
427
+ const outputFormat = options.outputFormat || 'jpg';
428
+ const outputFileTemplate = `frame-%03d.${outputFormat}`;
429
+ (0, fluent_ffmpeg_1.default)(videoPath)
430
+ .on('end', () => {
431
+ console.log('Frames extracted successfully.');
432
+ resolve(frames);
433
+ })
434
+ .on('error', (err) => {
435
+ console.error('Error extracting frames:', err.message);
436
+ reject(err);
437
+ })
438
+ .outputOptions([`-vf fps=1/${options.interval / 1000}`, `-q:v 2`]) // Set frame rate
439
+ .saveToFile(path_1.default.join(frameDir, outputFileTemplate)); // Save frames with a sequence number
440
+ fluent_ffmpeg_1.default.ffprobe(videoPath, (err, metadata) => {
441
+ if (err) {
442
+ return reject(err);
443
+ }
444
+ const duration = metadata?.format?.duration;
445
+ if (duration === undefined) {
446
+ return reject(new Error("Video duration not found in metadata."));
447
+ }
448
+ const totalFrames = Math.floor(duration * 1000 / options.interval);
449
+ for (let i = 0; i < totalFrames; i++) {
450
+ if (options.frameSelection && (i < (options.frameSelection.start || 0) || i > (options.frameSelection.end || totalFrames - 1))) {
451
+ continue;
452
+ }
453
+ frames.push({
454
+ source: path_1.default.join(frameDir, `frame-${String(i).padStart(3, '0')}.${outputFormat}`),
455
+ isRemote: false
456
+ });
457
+ }
458
+ });
459
+ }
460
+ return new Promise((resolve, reject) => {
461
+ processVideoExtraction(videoPath, frames, options, resolve, reject);
462
+ });
463
+ }
464
+ /**
465
+ * Sets a pattern on a specific area of a buffered image.
466
+ * @param {Buffer} buffer - The source image buffer.
467
+ * @param {Object} options - Options to customize the pattern.
468
+ * @returns {Promise<Buffer>} - The adjusted image buffer.
469
+ */
470
+ async setPatternBackground(buffer, options) {
471
+ // Load the buffered image onto a canvas
472
+ const img = await (0, canvas_1.loadImage)(buffer);
473
+ const canvas = (0, canvas_1.createCanvas)(img.width, img.height);
474
+ const ctx = canvas.getContext('2d');
475
+ ctx.drawImage(img, 0, 0);
476
+ // Set up the area to cover (default to full image if not specified)
477
+ const x = options.x || 0;
478
+ const y = options.y || 0;
479
+ const width = options.width || img.width;
480
+ const height = options.height || img.height;
481
+ ctx.save();
482
+ ctx.beginPath();
483
+ ctx.rect(x, y, width, height);
484
+ ctx.clip();
485
+ if (options.gradient) {
486
+ await this.fillWithGradient(ctx, width, height, options.gradient, x, y);
487
+ }
488
+ else if (options.backgroundColor) {
489
+ ctx.fillStyle = options.backgroundColor;
490
+ ctx.fillRect(x, y, width, height);
491
+ }
492
+ switch (options.type) {
493
+ case 'dots':
494
+ await this.drawDotsPattern(ctx, width, height, options, x, y);
495
+ break;
496
+ case 'stripes':
497
+ await this.drawStripesPattern(ctx, width, height, options, x, y);
498
+ break;
499
+ case 'grid':
500
+ await this.drawGridPattern(ctx, width, height, options, x, y);
501
+ break;
502
+ case 'checkerboard':
503
+ await this.drawCheckerboardPattern(ctx, width, height, options, x, y);
504
+ break;
505
+ case 'custom':
506
+ await this.drawCustomPattern(ctx, width, height, options, x, y);
507
+ break;
508
+ default:
509
+ throw new Error('Invalid pattern type specified.');
510
+ }
511
+ ctx.restore();
512
+ return canvas.toBuffer('image/png');
513
+ }
514
+ /**
515
+ * Fills the specified area with a gradient.
516
+ * @param ctx The rendering context of the canvas.
517
+ * @param width The width of the area to fill.
518
+ * @param height The height of the area to fill.
519
+ * @param gradient The gradient options.
520
+ * @param x The x offset of the area.
521
+ * @param y The y offset of the area.
522
+ */
523
+ async fillWithGradient(ctx, width, height, gradient, x = 0, y = 0) {
524
+ let grad;
525
+ if (gradient.type === 'linear') {
526
+ grad = ctx.createLinearGradient((gradient.startX || 0) + x, (gradient.startY || 0) + y, (gradient.endX || width) + x, (gradient.endY || height) + y);
527
+ }
528
+ else {
529
+ grad = ctx.createRadialGradient((gradient.startX || width / 2) + x, (gradient.startY || height / 2) + y, gradient.startRadius || 0, (gradient.endX || width / 2) + x, (gradient.endY || height / 2) + y, gradient.endRadius || Math.max(width, height));
530
+ }
531
+ gradient.colors.forEach(stop => {
532
+ grad.addColorStop(stop.stop, stop.color);
533
+ });
534
+ ctx.fillStyle = grad;
535
+ ctx.fillRect(x, y, width, height);
536
+ }
537
+ /**
538
+ * Draws a dots pattern in the specified area.
539
+ * @param ctx The rendering context of the canvas.
540
+ * @param width The width of the area.
541
+ * @param height The height of the area.
542
+ * @param options Options to customize the dot pattern.
543
+ * @param x The x offset of the area.
544
+ * @param y The y offset of the area.
545
+ */
546
+ async drawDotsPattern(ctx, width, height, options, x = 0, y = 0) {
547
+ const color = options.color || 'black';
548
+ const radius = options.size || 5;
549
+ const spacing = options.spacing || 10;
550
+ ctx.fillStyle = color;
551
+ for (let posY = y; posY < y + height; posY += spacing) {
552
+ for (let posX = x; posX < x + width; posX += spacing) {
553
+ ctx.beginPath();
554
+ ctx.arc(posX, posY, radius, 0, Math.PI * 2);
555
+ ctx.fill();
556
+ }
557
+ }
558
+ }
559
+ /**
560
+ * Draws a stripes pattern in the specified area.
561
+ * @param ctx The rendering context of the canvas.
562
+ * @param width The width of the area.
563
+ * @param height The height of the area.
564
+ * @param options Options to customize the stripes pattern.
565
+ * @param x The x offset of the area.
566
+ * @param y The y offset of the area.
567
+ */
568
+ async drawStripesPattern(ctx, width, height, options, x = 0, y = 0) {
569
+ const color = options.color || 'black';
570
+ const stripeWidth = options.size || 10;
571
+ const spacing = options.spacing || 20;
572
+ const angle = options.angle || 0;
573
+ ctx.fillStyle = color;
574
+ // Save the context to apply transformations and restore later
575
+ ctx.save();
576
+ // Translate and rotate the canvas
577
+ ctx.translate(x + width / 2, y + height / 2); // Move to the center of the area
578
+ ctx.rotate((angle * Math.PI) / 180); // Rotate by the specified angle
579
+ ctx.translate(-width / 2, -height / 2); // Translate back to top-left
580
+ // Draw the stripes pattern with the current transformations
581
+ for (let posX = 0; posX < width; posX += stripeWidth + spacing) {
582
+ ctx.fillRect(posX, 0, stripeWidth, height);
583
+ }
584
+ // Restore the original context to prevent transformations affecting other drawings
585
+ ctx.restore();
586
+ }
587
+ /**
588
+ * Draws a grid pattern in the specified area.
589
+ * @param ctx The rendering context of the canvas.
590
+ * @param width The width of the area.
591
+ * @param height The height of the area.
592
+ * @param options Options to customize the grid pattern.
593
+ * @param x The x offset of the area.
594
+ * @param y The y offset of the area.
595
+ */
596
+ async drawGridPattern(ctx, width, height, options, x = 0, y = 0) {
597
+ const color = options.color || 'black';
598
+ const size = options.size || 20;
599
+ ctx.strokeStyle = color;
600
+ ctx.lineWidth = 1;
601
+ for (let posX = x; posX <= x + width; posX += size) {
602
+ ctx.beginPath();
603
+ ctx.moveTo(posX, y);
604
+ ctx.lineTo(posX, y + height);
605
+ ctx.stroke();
606
+ }
607
+ for (let posY = y; posY <= y + height; posY += size) {
608
+ ctx.beginPath();
609
+ ctx.moveTo(x, posY);
610
+ ctx.lineTo(x + width, posY);
611
+ ctx.stroke();
612
+ }
613
+ }
614
+ /**
615
+ * Draws a checkerboard pattern in the specified area.
616
+ * @param ctx The rendering context of the canvas.
617
+ * @param width The width of the area.
618
+ * @param height The height of the area.
619
+ * @param options Options to customize the checkerboard pattern.
620
+ * @param x The x offset of the area.
621
+ * @param y The y offset of the area.
622
+ */
623
+ async drawCheckerboardPattern(ctx, width, height, options, x = 0, y = 0) {
624
+ const color1 = options.color || 'black';
625
+ const color2 = options.secondaryColor || 'white';
626
+ const size = options.size || 20;
627
+ for (let posY = y; posY < y + height; posY += size) {
628
+ for (let posX = x; posX < x + width; posX += size) {
629
+ ctx.fillStyle = (Math.floor(posX / size) + Math.floor(posY / size)) % 2 === 0 ? color1 : color2;
630
+ ctx.fillRect(posX, posY, size, size);
631
+ }
632
+ }
633
+ }
634
+ /**
635
+ * Draws a custom image pattern in the specified area.
636
+ * @param ctx The rendering context of the canvas.
637
+ * @param width The width of the area.
638
+ * @param height The height of the area.
639
+ * @param options Options to customize the custom pattern.
640
+ * @param x The x offset of the area.
641
+ * @param y The y offset of the area.
642
+ */
643
+ async drawCustomPattern(ctx, width, height, options, x = 0, y = 0) {
644
+ if (!options.customPatternImage) {
645
+ throw new Error('Custom pattern image path is required for custom patterns.');
646
+ }
647
+ const patternImage = await (0, canvas_1.loadImage)(options.customPatternImage);
648
+ const pattern = ctx.createPattern(patternImage, 'repeat');
649
+ ctx.fillStyle = pattern;
650
+ ctx.translate(x, y);
651
+ ctx.fillRect(0, 0, width, height);
652
+ ctx.resetTransform();
653
+ }
654
+ async animate(frames, defaultDuration, defaultWidth = 800, defaultHeight = 600, options) {
655
+ const buffers = [];
656
+ const isNode = typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
657
+ if (options?.onStart)
658
+ options.onStart();
659
+ let encoder = null;
660
+ let gifStream = null;
661
+ if (options?.gif) {
662
+ if (!options.gifPath) {
663
+ throw new Error("GIF generation enabled but no gifPath provided.");
664
+ }
665
+ encoder = new gifencoder_1.default(defaultWidth, defaultHeight);
666
+ gifStream = fs_1.default.createWriteStream(options.gifPath);
667
+ encoder.createReadStream().pipe(gifStream);
668
+ encoder.start();
669
+ encoder.setRepeat(0);
670
+ encoder.setQuality(10);
671
+ }
672
+ for (let i = 0; i < frames.length; i++) {
673
+ const frame = frames[i];
674
+ const width = frame.width || defaultWidth;
675
+ const height = frame.height || defaultHeight;
676
+ const canvas = (0, canvas_1.createCanvas)(width, height);
677
+ const ctx = canvas.getContext('2d');
678
+ if (!isNode) {
679
+ canvas.width = width;
680
+ canvas.height = height;
681
+ document.body.appendChild(canvas);
682
+ }
683
+ ctx.clearRect(0, 0, width, height);
684
+ if (frame.transformations) {
685
+ const { scaleX = 1, scaleY = 1, rotate = 0, translateX = 0, translateY = 0 } = frame.transformations;
686
+ ctx.save();
687
+ ctx.translate(translateX, translateY);
688
+ ctx.rotate((rotate * Math.PI) / 180);
689
+ ctx.scale(scaleX, scaleY);
690
+ }
691
+ let fillStyle = null;
692
+ if (frame.gradient) {
693
+ const { type, startX, startY, endX, endY, startRadius, endRadius, colors } = frame.gradient;
694
+ let gradient = null;
695
+ if (type === 'linear') {
696
+ gradient = ctx.createLinearGradient(startX || 0, startY || 0, endX || width, endY || height);
697
+ }
698
+ else if (type === 'radial') {
699
+ gradient = ctx.createRadialGradient(startX || width / 2, startY || height / 2, startRadius || 0, endX || width / 2, endY || height / 2, endRadius || Math.max(width, height));
700
+ }
701
+ colors.forEach(colorStop => {
702
+ if (gradient)
703
+ gradient.addColorStop(colorStop.stop, colorStop.color);
704
+ });
705
+ fillStyle = gradient;
706
+ }
707
+ if (frame.pattern) {
708
+ const patternImage = await (0, canvas_1.loadImage)(frame.pattern.imagePath);
709
+ const pattern = ctx.createPattern(patternImage, frame.pattern.repeat || 'repeat');
710
+ fillStyle = pattern;
711
+ }
712
+ if (!fillStyle && frame.backgroundColor) {
713
+ fillStyle = frame.backgroundColor;
714
+ }
715
+ if (fillStyle) {
716
+ ctx.fillStyle = fillStyle;
717
+ ctx.fillRect(0, 0, width, height);
718
+ }
719
+ if (frame.imagePath) {
720
+ const image = await (0, canvas_1.loadImage)(frame.imagePath);
721
+ ctx.globalCompositeOperation = frame.blendMode || 'source-over';
722
+ ctx.drawImage(image, 0, 0, width, height);
723
+ }
724
+ if (frame.onDrawCustom) {
725
+ frame.onDrawCustom(ctx, canvas);
726
+ }
727
+ if (frame.transformations) {
728
+ ctx.restore();
729
+ }
730
+ const buffer = canvas.toBuffer('image/png');
731
+ buffers.push(buffer);
732
+ if (encoder) {
733
+ const frameDuration = frame.duration || defaultDuration;
734
+ encoder.setDelay(frameDuration);
735
+ encoder.addFrame(ctx);
736
+ }
737
+ if (options?.onFrame)
738
+ options.onFrame(i);
739
+ await new Promise(resolve => setTimeout(resolve, frame.duration || defaultDuration));
740
+ }
741
+ if (encoder) {
742
+ encoder.finish();
743
+ }
744
+ if (options?.onEnd)
745
+ options.onEnd();
746
+ return options?.gif ? undefined : buffers;
747
+ }
424
748
  validHex(hexColor) {
425
749
  const hexPattern = /^#[0-9a-fA-F]{6}$/;
426
750
  if (!hexPattern.test(hexColor)) {