founders card
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { createRequire } from "node:module";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import type { DrawStyle } from "@/lib/card-gen/id-config";
|
||||
|
||||
export type GenerateTask = {
|
||||
text: string;
|
||||
outPath: string;
|
||||
};
|
||||
|
||||
export type WorkerFont = {
|
||||
path: string;
|
||||
family: string;
|
||||
};
|
||||
|
||||
type WorkerMsg =
|
||||
| { type: "progress" }
|
||||
| { type: "error"; message: string };
|
||||
|
||||
export async function generatePngsInWorkers(args: {
|
||||
imagePath: string;
|
||||
fonts: WorkerFont[];
|
||||
style: DrawStyle;
|
||||
tasks: GenerateTask[];
|
||||
onProgress: (current: number, total: number) => void;
|
||||
}): Promise<void> {
|
||||
const { imagePath, fonts, style, tasks, onProgress } = args;
|
||||
if (tasks.length === 0) return;
|
||||
|
||||
const require = createRequire(path.join(process.cwd(), "package.json"));
|
||||
const threads = require("node:worker_threads") as typeof import("node:worker_threads");
|
||||
|
||||
const workerCount = Math.max(
|
||||
1,
|
||||
Math.min(os.cpus().length || 1, 8, tasks.length),
|
||||
);
|
||||
const buckets: GenerateTask[][] = Array.from(
|
||||
{ length: workerCount },
|
||||
() => [],
|
||||
);
|
||||
tasks.forEach((task, i) => {
|
||||
buckets[i % workerCount]!.push(task);
|
||||
});
|
||||
|
||||
const workerPath = path.join(process.cwd(), "workers", "generate-id.cjs");
|
||||
let completed = 0;
|
||||
const total = tasks.length;
|
||||
|
||||
await Promise.all(
|
||||
buckets
|
||||
.filter((bucket) => bucket.length > 0)
|
||||
.map(
|
||||
(bucket) =>
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const worker = new threads.Worker(workerPath, {
|
||||
workerData: {
|
||||
imagePath,
|
||||
fonts,
|
||||
style,
|
||||
tasks: bucket,
|
||||
},
|
||||
});
|
||||
let settled = false;
|
||||
const fail = (err: Error) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
void worker.terminate();
|
||||
reject(err);
|
||||
};
|
||||
worker.on("message", (msg: WorkerMsg) => {
|
||||
if (msg?.type === "progress") {
|
||||
completed += 1;
|
||||
onProgress(completed, total);
|
||||
return;
|
||||
}
|
||||
if (msg?.type === "error") {
|
||||
fail(new Error(msg.message || "Worker error"));
|
||||
}
|
||||
});
|
||||
worker.on("error", (err) => fail(err));
|
||||
worker.on("exit", (code) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
if (code === 0) resolve();
|
||||
else reject(new Error(`Worker exited with code ${code}`));
|
||||
});
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user