mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
refactor(api): remove mode, add colorizeStrength, lower denoise default to 25
This commit is contained in:
@@ -19,13 +19,13 @@ import { updateSingleFileProgress } from "../progress.js";
|
|||||||
import { registerToolProcessFn } from "../tool-factory.js";
|
import { registerToolProcessFn } from "../tool-factory.js";
|
||||||
|
|
||||||
const settingsSchema = z.object({
|
const settingsSchema = z.object({
|
||||||
mode: z.enum(["auto", "light", "heavy"]).default("auto"),
|
|
||||||
scratchRemoval: z.boolean().default(true),
|
scratchRemoval: z.boolean().default(true),
|
||||||
faceEnhancement: z.boolean().default(true),
|
faceEnhancement: z.boolean().default(true),
|
||||||
fidelity: z.number().min(0).max(1).default(0.7),
|
fidelity: z.number().min(0).max(1).default(0.7),
|
||||||
denoise: z.boolean().default(true),
|
denoise: z.boolean().default(true),
|
||||||
denoiseStrength: z.number().min(0).max(100).default(40),
|
denoiseStrength: z.number().min(0).max(100).default(25),
|
||||||
colorize: z.boolean().default(false),
|
colorize: z.boolean().default(false),
|
||||||
|
colorizeStrength: z.number().min(0).max(100).default(85),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,10 +153,7 @@ export function registerRestorePhoto(app: FastifyInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const log = request.log;
|
const log = request.log;
|
||||||
log.info(
|
log.info({ toolId: "restore-photo", imageSize: originalSize }, "Starting photo restoration");
|
||||||
{ toolId: "restore-photo", imageSize: originalSize, mode: settings.mode },
|
|
||||||
"Starting photo restoration",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reply immediately so the HTTP connection closes within proxy timeout limits.
|
// Reply immediately so the HTTP connection closes within proxy timeout limits.
|
||||||
// The result will be delivered via the SSE progress channel.
|
// The result will be delivered via the SSE progress channel.
|
||||||
@@ -178,13 +175,13 @@ export function registerRestorePhoto(app: FastifyInstance) {
|
|||||||
fileBuffer,
|
fileBuffer,
|
||||||
join(workspacePath, "output"),
|
join(workspacePath, "output"),
|
||||||
{
|
{
|
||||||
mode: settings.mode,
|
|
||||||
scratchRemoval: settings.scratchRemoval,
|
scratchRemoval: settings.scratchRemoval,
|
||||||
faceEnhancement: settings.faceEnhancement,
|
faceEnhancement: settings.faceEnhancement,
|
||||||
fidelity: settings.fidelity,
|
fidelity: settings.fidelity,
|
||||||
denoise: settings.denoise,
|
denoise: settings.denoise,
|
||||||
denoiseStrength: settings.denoiseStrength,
|
denoiseStrength: settings.denoiseStrength,
|
||||||
colorize: settings.colorize,
|
colorize: settings.colorize,
|
||||||
|
colorizeStrength: settings.colorizeStrength,
|
||||||
},
|
},
|
||||||
onProgress,
|
onProgress,
|
||||||
);
|
);
|
||||||
@@ -257,13 +254,13 @@ export function registerRestorePhoto(app: FastifyInstance) {
|
|||||||
registerToolProcessFn({
|
registerToolProcessFn({
|
||||||
toolId: "restore-photo",
|
toolId: "restore-photo",
|
||||||
settingsSchema: z.object({
|
settingsSchema: z.object({
|
||||||
mode: z.enum(["auto", "light", "heavy"]).default("auto"),
|
|
||||||
scratchRemoval: z.boolean().default(true),
|
scratchRemoval: z.boolean().default(true),
|
||||||
faceEnhancement: z.boolean().default(true),
|
faceEnhancement: z.boolean().default(true),
|
||||||
fidelity: z.number().min(0).max(1).default(0.7),
|
fidelity: z.number().min(0).max(1).default(0.7),
|
||||||
denoise: z.boolean().default(true),
|
denoise: z.boolean().default(true),
|
||||||
denoiseStrength: z.number().min(0).max(100).default(40),
|
denoiseStrength: z.number().min(0).max(100).default(25),
|
||||||
colorize: z.boolean().default(false),
|
colorize: z.boolean().default(false),
|
||||||
|
colorizeStrength: z.number().min(0).max(100).default(85),
|
||||||
}),
|
}),
|
||||||
process: async (inputBuffer, settings, filename) => {
|
process: async (inputBuffer, settings, filename) => {
|
||||||
const s = settings as z.infer<typeof settingsSchema>;
|
const s = settings as z.infer<typeof settingsSchema>;
|
||||||
@@ -271,13 +268,13 @@ export function registerRestorePhoto(app: FastifyInstance) {
|
|||||||
const jobId = randomUUID();
|
const jobId = randomUUID();
|
||||||
const workspacePath = await createWorkspace(jobId);
|
const workspacePath = await createWorkspace(jobId);
|
||||||
const result = await restorePhoto(orientedBuffer, join(workspacePath, "output"), {
|
const result = await restorePhoto(orientedBuffer, join(workspacePath, "output"), {
|
||||||
mode: s.mode,
|
|
||||||
scratchRemoval: s.scratchRemoval,
|
scratchRemoval: s.scratchRemoval,
|
||||||
faceEnhancement: s.faceEnhancement,
|
faceEnhancement: s.faceEnhancement,
|
||||||
fidelity: s.fidelity,
|
fidelity: s.fidelity,
|
||||||
denoise: s.denoise,
|
denoise: s.denoise,
|
||||||
denoiseStrength: s.denoiseStrength,
|
denoiseStrength: s.denoiseStrength,
|
||||||
colorize: s.colorize,
|
colorize: s.colorize,
|
||||||
|
colorizeStrength: s.colorizeStrength,
|
||||||
});
|
});
|
||||||
const outputFormat = await resolveOutputFormat(inputBuffer, filename);
|
const outputFormat = await resolveOutputFormat(inputBuffer, filename);
|
||||||
let outputBuffer = result.buffer;
|
let outputBuffer = result.buffer;
|
||||||
|
|||||||
@@ -83,17 +83,16 @@ describe("Restore Photo", () => {
|
|||||||
}
|
}
|
||||||
}, 60_000);
|
}, 60_000);
|
||||||
|
|
||||||
it("accepts auto mode with all features enabled", async () => {
|
it("accepts all features enabled", async () => {
|
||||||
const { body, contentType } = createMultipartPayload([
|
const { body, contentType } = createMultipartPayload([
|
||||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
{
|
{
|
||||||
name: "settings",
|
name: "settings",
|
||||||
content: JSON.stringify({
|
content: JSON.stringify({
|
||||||
mode: "auto",
|
|
||||||
scratchRemoval: true,
|
scratchRemoval: true,
|
||||||
faceEnhancement: true,
|
faceEnhancement: true,
|
||||||
denoise: true,
|
denoise: true,
|
||||||
denoiseStrength: 40,
|
denoiseStrength: 25,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
@@ -111,14 +110,14 @@ describe("Restore Photo", () => {
|
|||||||
expect([202, 501]).toContain(res.statusCode);
|
expect([202, 501]).toContain(res.statusCode);
|
||||||
}, 60_000);
|
}, 60_000);
|
||||||
|
|
||||||
it("accepts heavy mode with colorize enabled", async () => {
|
it("accepts colorize with custom strength", async () => {
|
||||||
const { body, contentType } = createMultipartPayload([
|
const { body, contentType } = createMultipartPayload([
|
||||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
{
|
{
|
||||||
name: "settings",
|
name: "settings",
|
||||||
content: JSON.stringify({
|
content: JSON.stringify({
|
||||||
mode: "heavy",
|
|
||||||
colorize: true,
|
colorize: true,
|
||||||
|
colorizeStrength: 50,
|
||||||
fidelity: 0.9,
|
fidelity: 0.9,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -137,13 +136,12 @@ describe("Restore Photo", () => {
|
|||||||
expect([202, 501]).toContain(res.statusCode);
|
expect([202, 501]).toContain(res.statusCode);
|
||||||
}, 60_000);
|
}, 60_000);
|
||||||
|
|
||||||
it("accepts light mode with features disabled", async () => {
|
it("accepts all features disabled", async () => {
|
||||||
const { body, contentType } = createMultipartPayload([
|
const { body, contentType } = createMultipartPayload([
|
||||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
{
|
{
|
||||||
name: "settings",
|
name: "settings",
|
||||||
content: JSON.stringify({
|
content: JSON.stringify({
|
||||||
mode: "light",
|
|
||||||
scratchRemoval: false,
|
scratchRemoval: false,
|
||||||
faceEnhancement: false,
|
faceEnhancement: false,
|
||||||
denoise: false,
|
denoise: false,
|
||||||
@@ -273,12 +271,12 @@ describe("Restore Photo", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects invalid mode value", async () => {
|
it("rejects colorizeStrength out of range", async () => {
|
||||||
const { body, contentType } = createMultipartPayload([
|
const { body, contentType } = createMultipartPayload([
|
||||||
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
{
|
{
|
||||||
name: "settings",
|
name: "settings",
|
||||||
content: JSON.stringify({ mode: "turbo" }),
|
content: JSON.stringify({ colorizeStrength: 150 }),
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -366,4 +364,26 @@ describe("Restore Photo", () => {
|
|||||||
|
|
||||||
expect(res.statusCode).toBe(401);
|
expect(res.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("ignores old mode field gracefully", async () => {
|
||||||
|
const { body, contentType } = createMultipartPayload([
|
||||||
|
{ name: "file", filename: "test.png", contentType: "image/png", content: PNG },
|
||||||
|
{
|
||||||
|
name: "settings",
|
||||||
|
content: JSON.stringify({ mode: "heavy", scratchRemoval: true }),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const res = await app.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: "/api/v1/tools/restore-photo",
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${adminToken}`,
|
||||||
|
"content-type": contentType,
|
||||||
|
},
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect([202, 501]).toContain(res.statusCode);
|
||||||
|
}, 60_000);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user