feat(canvas): 图片转换插件 - 增加本地媒体转换节点与运行时

This commit is contained in:
QAyong
2026-09-08 22:55:44 +08:00
parent 8360b2c933
commit f755d6db97
51 changed files with 3284 additions and 44 deletions

View File

@@ -0,0 +1,36 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { createDepthEstimationHttpModule, DepthRuntimeError, resolveDepthRuntimePaths, validateDepthDataUrl } from "../src/modules/depth-estimation-http.js";
test("depth runtime module declares signed status and run routes", () => {
const module = createDepthEstimationHttpModule();
assert.equal(module.descriptor.id, "depth-estimation");
assert.deepEqual(module.descriptor.scopes, ["depth:status", "depth:run"]);
assert.deepEqual(module.routes.map((route) => `${route.method} ${route.path}`), [
"GET /depth-estimation/status",
"POST /depth-estimation/run",
]);
module.dispose?.();
});
test("depth input guard accepts image data URLs and rejects unsafe input", () => {
assert.doesNotThrow(() => validateDepthDataUrl("data:image/png;base64,AQID"));
assert.throws(
() => validateDepthDataUrl("data:text/plain;base64,AQID"),
(error: unknown) => error instanceof DepthRuntimeError && error.code === "depth_input_invalid" && error.statusCode === 400,
);
});
test("depth runtime paths honor explicit project and Python overrides", () => {
const paths = resolveDepthRuntimePaths({
CANVAS_PROJECT_ROOT: "C:\\depth-project",
CANVAS_DEPTH_PYTHON: "C:\\depth-project\\venv\\python.exe",
CANVAS_DEPTH_HF_HOME: "C:\\depth-cache",
CANVAS_DEPTH_MODEL_ID: "example/depth-model",
});
assert.equal(paths.pythonPath, "C:\\depth-project\\venv\\python.exe");
assert.equal(paths.hfHome, "C:\\depth-cache");
assert.equal(paths.modelId, "example/depth-model");
assert.equal(paths.configured, false);
});

View File

@@ -0,0 +1,36 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { createLineartEstimationHttpModule, LineartRuntimeError, resolveLineartRuntimePaths, validateLineartDataUrl } from "../src/modules/lineart-estimation-http.js";
test("lineart runtime module declares signed status and run routes", () => {
const module = createLineartEstimationHttpModule();
assert.equal(module.descriptor.id, "lineart-estimation");
assert.deepEqual(module.descriptor.scopes, ["lineart:status", "lineart:run"]);
assert.deepEqual(module.routes.map((route) => `${route.method} ${route.path}`), [
"GET /lineart-estimation/status",
"POST /lineart-estimation/run",
]);
module.dispose?.();
});
test("lineart input guard accepts image data URLs and rejects unsafe input", () => {
assert.doesNotThrow(() => validateLineartDataUrl("data:image/png;base64,AQID"));
assert.throws(
() => validateLineartDataUrl("data:text/plain;base64,AQID"),
(error: unknown) => error instanceof LineartRuntimeError && error.code === "lineart_input_invalid" && error.statusCode === 400,
);
});
test("lineart runtime paths honor explicit project and Python overrides", () => {
const paths = resolveLineartRuntimePaths({
CANVAS_PROJECT_ROOT: "C:\\lineart-project",
CANVAS_DEPTH_PYTHON: "C:\\lineart-project\\venv\\python.exe",
CANVAS_LINEART_HF_HOME: "C:\\lineart-cache",
CANVAS_LINEART_MODEL_ID: "example/lineart-model",
});
assert.equal(paths.pythonPath, "C:\\lineart-project\\venv\\python.exe");
assert.equal(paths.hfHome, "C:\\lineart-cache");
assert.equal(paths.modelId, "example/lineart-model");
assert.equal(paths.configured, false);
});

View File

@@ -0,0 +1,37 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { createPoseEstimationHttpModule, PoseRuntimeError, resolvePoseRuntimePaths, validatePoseDataUrl } from "../src/modules/pose-estimation-http.js";
test("pose runtime module declares signed status and run routes", () => {
const module = createPoseEstimationHttpModule();
assert.equal(module.descriptor.id, "pose-estimation");
assert.deepEqual(module.descriptor.scopes, ["pose:status", "pose:run"]);
assert.deepEqual(module.routes.map((route) => `${route.method} ${route.path}`), [
"GET /pose-estimation/status",
"POST /pose-estimation/run",
]);
module.dispose?.();
});
test("pose input guard accepts image data URLs and rejects unsafe input", () => {
assert.doesNotThrow(() => validatePoseDataUrl("data:image/png;base64,AQID"));
assert.throws(
() => validatePoseDataUrl("data:text/plain;base64,AQID"),
(error: unknown) => error instanceof PoseRuntimeError && error.code === "pose_input_invalid" && error.statusCode === 400,
);
});
test("pose runtime paths honor explicit project and Python overrides", () => {
const paths = resolvePoseRuntimePaths({
CANVAS_PROJECT_ROOT: "C:\\pose-project",
CANVAS_POSE_PYTHON: "C:\\pose-project\\venv\\python.exe",
CANVAS_POSE_HF_HOME: "C:\\pose-cache",
CANVAS_POSE_MODEL_ID: "example/pose-model",
});
assert.equal(paths.pythonPath, "C:\\pose-project\\venv\\python.exe");
assert.equal(paths.hfHome, "C:\\pose-cache");
assert.equal(paths.modelId, "example/pose-model");
assert.equal(paths.configured, false);
assert.equal(paths.modelCached, false);
});