feat: add support for JXL, Camera RAW, ICO, TGA, PSD, EXR, HDR image formats

Extends the platform to handle 7 new image format families alongside
the existing AVIF support gap-fill. Uses the established HEIC decoder
pattern (CLI decode → PNG → Sharp) for formats Sharp can't handle
natively: Camera RAW via dcraw_emu/LibRaw, PSD/TGA/EXR/HDR via
ImageMagick. JXL and ICO are Sharp-native. Adds server-side preview
for non-browser-displayable formats and JXL as a new convert output
target. All 27 validateImageBuffer callers updated with filename for
extension-based format detection.
This commit is contained in:
ashim-hq
2026-04-21 09:59:57 +08:00
parent e94ac945bb
commit 2aadb66031
39 changed files with 583 additions and 43 deletions
+7 -1
View File
@@ -20,6 +20,7 @@ import { formatZodErrors } from "../lib/errors.js";
import { isToolInstalled } from "../lib/feature-status.js";
import { validateImageBuffer } from "../lib/file-validation.js";
import { sanitizeFilename } from "../lib/filename.js";
import { decodeToSharpCompat, needsCliDecode } from "../lib/format-decoders.js";
import { decodeHeic } from "../lib/heic-converter.js";
import { type JobProgress, updateJobProgress } from "./progress.js";
import { getToolConfig } from "./tool-factory.js";
@@ -144,7 +145,7 @@ export async function registerBatchRoutes(app: FastifyInstance): Promise<void> {
updateJobProgress({ ...progress });
// Validate the image
const validation = await validateImageBuffer(file.buffer);
const validation = await validateImageBuffer(file.buffer, file.filename);
if (!validation.valid) {
progress.failedFiles++;
progress.errors.push({
@@ -167,6 +168,11 @@ export async function registerBatchRoutes(app: FastifyInstance): Promise<void> {
const ext = processFilename.match(/\.[^.]+$/)?.[0];
if (ext) processFilename = `${processFilename.slice(0, -ext.length)}.png`;
}
if (!skipPreprocess && needsCliDecode(validation.format)) {
processBuffer = await decodeToSharpCompat(processBuffer, validation.format);
const ext = processFilename.match(/\.[^.]+$/)?.[0];
if (ext) processFilename = `${processFilename.slice(0, -ext.length)}.png`;
}
if (!skipPreprocess) {
processBuffer = await autoOrient(processBuffer);
}