mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
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.
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import sharp from "sharp";
|
|
|
|
export interface OutputFormat {
|
|
format: keyof sharp.FormatEnum;
|
|
extension: string;
|
|
contentType: string;
|
|
quality: number;
|
|
}
|
|
|
|
const FORMAT_MAP: Record<
|
|
string,
|
|
{ format: keyof sharp.FormatEnum; extension: string; contentType: string }
|
|
> = {
|
|
jpeg: { format: "jpeg", extension: "jpg", contentType: "image/jpeg" },
|
|
png: { format: "png", extension: "png", contentType: "image/png" },
|
|
webp: { format: "webp", extension: "webp", contentType: "image/webp" },
|
|
gif: { format: "gif", extension: "gif", contentType: "image/gif" },
|
|
tiff: { format: "tiff", extension: "tiff", contentType: "image/tiff" },
|
|
avif: { format: "avif", extension: "avif", contentType: "image/avif" },
|
|
heif: { format: "avif", extension: "avif", contentType: "image/avif" },
|
|
jxl: { format: "png", extension: "png", contentType: "image/png" },
|
|
};
|
|
|
|
const DEFAULT_QUALITY = 95;
|
|
const PNG_FALLBACK = FORMAT_MAP.png;
|
|
|
|
/** Formats that have no Sharp output encoder — fall back to PNG. */
|
|
const PNG_FALLBACK_FORMATS = new Set(["svg", "bmp", "raw", "tga", "psd", "exr", "hdr", "ico"]);
|
|
|
|
/**
|
|
* Detect the input image format and return matching output config.
|
|
* Falls back to PNG for undetectable or unsupported output formats
|
|
* (SVG, BMP, Camera RAW, TGA, PSD, EXR, HDR, ICO).
|
|
*/
|
|
export async function resolveOutputFormat(
|
|
inputBuffer: Buffer,
|
|
_filename: string,
|
|
qualityOverride?: number,
|
|
): Promise<OutputFormat> {
|
|
let detected: string | undefined;
|
|
try {
|
|
const meta = await sharp(inputBuffer).metadata();
|
|
detected = meta.format;
|
|
} catch {
|
|
// format detection failed
|
|
}
|
|
|
|
// Force PNG fallback for formats without a Sharp output encoder
|
|
if (detected && PNG_FALLBACK_FORMATS.has(detected)) {
|
|
detected = undefined;
|
|
}
|
|
|
|
const mapped = detected ? FORMAT_MAP[detected] : undefined;
|
|
const config = mapped ?? PNG_FALLBACK;
|
|
const quality = qualityOverride ?? DEFAULT_QUALITY;
|
|
|
|
return { ...config, quality };
|
|
}
|