mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import type { FastifyInstance } from "fastify";
|
|
import sharp from "sharp";
|
|
import { z } from "zod";
|
|
import { createToolRoute } from "../tool-factory.js";
|
|
|
|
const settingsSchema = z.object({
|
|
width: z.number().min(1).max(4096).optional(),
|
|
height: z.number().min(1).max(4096).optional(),
|
|
extractFrame: z.number().min(0).optional(),
|
|
optimize: z.boolean().default(false),
|
|
});
|
|
|
|
export function registerGifTools(app: FastifyInstance) {
|
|
createToolRoute(app, {
|
|
toolId: "gif-tools",
|
|
settingsSchema,
|
|
process: async (inputBuffer, settings, filename) => {
|
|
if (settings.extractFrame !== undefined) {
|
|
// Extract a single frame from animated GIF
|
|
const image = sharp(inputBuffer, { page: settings.extractFrame });
|
|
|
|
if (settings.width || settings.height) {
|
|
image.resize(settings.width, settings.height, { fit: "inside" });
|
|
}
|
|
|
|
const buffer = await image.png().toBuffer();
|
|
const outName = `${filename.replace(/\.gif$/i, "")}_frame${settings.extractFrame}.png`;
|
|
return { buffer, filename: outName, contentType: "image/png" };
|
|
}
|
|
|
|
// Process animated GIF (preserve animation)
|
|
const image = sharp(inputBuffer, { animated: true });
|
|
|
|
if (settings.width || settings.height) {
|
|
image.resize(settings.width, settings.height, { fit: "inside" });
|
|
}
|
|
|
|
if (settings.optimize) {
|
|
// Reduce colors for optimization
|
|
image.gif({ effort: 10 });
|
|
}
|
|
|
|
const buffer = await image.gif().toBuffer();
|
|
return { buffer, filename, contentType: "image/gif" };
|
|
},
|
|
});
|
|
}
|