Integrate worker to offload main thread from heavy tasks

This commit is contained in:
2025-11-11 15:13:38 +01:00
parent 0f1fff0609
commit 0451a1c960
8 changed files with 115 additions and 205 deletions

View File

@@ -0,0 +1,108 @@
import fs from "fs";
import path from "path";
import { createCanvas, loadImage, registerFont } from "canvas";
export class NameCardCreator {
constructor(templatePath) {
this.templatePath = templatePath;
this.loadFont("./wwwroot/assets/fonts/Fredoka/static/Fredoka-Bold.ttf")
}
loadFont(fontPath) {
const fullPath = path.resolve(fontPath);
if (!fs.existsSync(fullPath)) {
throw new Error(`Font file not found at ${fullPath}`);
}
registerFont(fullPath, {family: "Fredoka Bold"});
}
/**
* Crée la carte de bienvenue entièrement via Canvas
*/
async getWelcomeCard(avatarPath, name) {
try {
const canvasWidth = 3000;
const canvasHeight = 1000;
const canvas = createCanvas(canvasWidth, canvasHeight);
const ctx = canvas.getContext("2d");
const template = await loadImage(this.templatePath);
ctx.drawImage(template, 0, 0, canvasWidth, canvasHeight);
const avatarSize = 675;
const avatar = await loadImage(avatarPath);
const avatarX = 225;
const avatarY = (canvasHeight - avatarSize) / 2;
ctx.save();
ctx.beginPath();
ctx.arc(
avatarX + avatarSize / 2,
avatarY + avatarSize / 2,
avatarSize / 2,
0,
Math.PI * 2,
true
);
ctx.closePath();
ctx.clip();
ctx.drawImage(avatar, avatarX, avatarY, avatarSize, avatarSize);
ctx.restore();
ctx.lineWidth = 8;
ctx.strokeStyle = "#ffffff";
ctx.beginPath();
ctx.arc(
avatarX + avatarSize / 2,
avatarY + avatarSize / 2,
avatarSize / 2,
0,
Math.PI * 2,
true
);
ctx.stroke();
const messageX = avatarX + 1525;
const messageY = (canvasHeight / 2)+75;
ctx.fillStyle = "#ede6e6";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.font = '115px "Fredoka Bold"';
const lines = [`Welcome ${name}, to the`, "Spicy Jail ~"];
const lineHeight = 130;
const startY = messageY - ((lines.length - 1) / 2) * lineHeight;
lines.forEach((line, i) => {
ctx.fillText(line, messageX, startY + i * lineHeight);
});
const result = this.scallDown(canvas, 1500, 500)
fs.writeFileSync("./tests/result/namecard.png", result);
console.log("✅ Name card created: namecard.png");
return result;
} catch (err) {
console.error("Error creating name card:", err);
throw err;
}
}
/**
*
* @param target
* @param width
* @param height
* @returns {Buffer}
*/
scallDown(target, width, height) {
const smallCanvas = createCanvas(width, height);
const smallCtx = smallCanvas.getContext('2d');
smallCtx.drawImage(target, 0, 0, width, height);
return smallCanvas.toBuffer();
}
}

View File

@@ -0,0 +1,27 @@
import { parentPort } from 'worker_threads';
import {NameCardCreator} from "./nameCardCreator.js";
parentPort.on("message", async (data) => {
try {
const { templatePath, avatarURL, username } = data;
const creator = new NameCardCreator(templatePath);
const buffer = await creator.getWelcomeCard(avatarURL, username);
parentPort.postMessage({ result: buffer });
} catch (err) {
parentPort.postMessage({ error: err.message });
}
});
/*(async () => {
try {
const { creator, avatarURL, userName } = workerData;
const buffer = await creator.getWelcomeCard(avatarURL, userName);
parentPort.postMessage(buffer); // return the buffer to main thread
} catch (err) {
parentPort.postMessage({ error: err.message });
}
})();*/

View File

@@ -0,0 +1,31 @@
import path from "path";
import { Worker } from 'worker_threads';
import {Logger} from "../logging/logger.js";
function launchWorker({templatePath, avatarURL, username}) {
return new Promise((resolve, reject) => {
const workerFile = path.resolve('./wwwroot/core/namecards/nameCardWorker.js');
const worker = new Worker(workerFile);
worker.postMessage({templatePath, avatarURL, username});
worker.on('message', (result) => {
resolve(result);
worker.terminate()
.catch(err => Logger.error("Unable to terminate worker", err.message));
});
worker.on('error', (err) => {
reject(err);
worker.terminate()
.catch(err => Logger.error("Unable to terminate worker", err.message));
});
worker.on('exit', (code) => {
if (code !== 0) reject(new Error(`Worker stopped with code ${code}`));
});
});
}
export {launchWorker}